Vulnerabilities, React 19, Devexpress 26.1.4
This commit is contained in:
parent
f949d0ae78
commit
ee121db521
100 changed files with 1839 additions and 3155 deletions
|
|
@ -10,8 +10,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DevExpress.AspNetCore.Reporting" Version="25.1.7" />
|
||||
<PackageReference Include="DevExpress.Drawing.Skia" Version="25.1.7" />
|
||||
<!-- DevExpress rapor motorunda client (ui/package.json) ve server sürümü BİREBİR
|
||||
aynı olmak zorundadır; farklı olursa tasarımcı kaydetme ve viewer preview
|
||||
serileştirmesi bozulur. İkisini birlikte güncelleyin. -->
|
||||
<PackageReference Include="DevExpress.AspNetCore.Reporting" Version="26.1.4" />
|
||||
<PackageReference Include="DevExpress.Drawing.Skia" Version="26.1.4" />
|
||||
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.Autofac" Version="10.0.0" />
|
||||
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="10.0.0" />
|
||||
|
|
@ -29,6 +32,12 @@
|
|||
<PackageReference Include="Serilog.Sinks.PostgreSQL" Version="2.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="8.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="10.0.0" />
|
||||
<!-- SignalR Redis backplane'i MessagePack 2.5.187'yi geçişli olarak getiriyor;
|
||||
o sürümde 2 yüksek + 9 orta seviye açık var (NU1902/NU1903). NuGet'te
|
||||
override yok, geçişli sürümü ezmenin yolu doğrudan referans vermek.
|
||||
2.5.302 açıkları kapatan ilk 2.x sürümü ve SignalR'ın istediği
|
||||
">= 2.5.187" aralığını karşılıyor. 3.x kırıcı, bilerek geçilmedi. -->
|
||||
<PackageReference Include="MessagePack" Version="2.5.302" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
ARG ENV="dev"
|
||||
|
||||
FROM node:22-alpine
|
||||
FROM node:24-alpine
|
||||
ARG ENV=$ENV
|
||||
RUN apk add --no-cache ca-certificates && update-ca-certificates
|
||||
|
||||
|
|
@ -8,8 +8,10 @@ ENV GENERATE_SOURCEMAP=false
|
|||
ENV NODE_OPTIONS=--max-old-space-size=16384
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
# Vite 8 rolldown ile bundle ediyor; workbox-build hâlâ rollup çekiyor.
|
||||
# Alpine (musl) native binding'leri lockfile'da optional olduğu için ikisi de elle kuruluyor.
|
||||
RUN npm i && \
|
||||
npm install @rollup/rollup-linux-x64-musl --save-dev && \
|
||||
npm install @rolldown/binding-linux-x64-musl @rollup/rollup-linux-x64-musl --save-dev && \
|
||||
npm rebuild rollup
|
||||
COPY . .
|
||||
RUN npm run build:production -- --mode $ENV
|
||||
|
|
|
|||
3584
ui/package-lock.json
generated
3584
ui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
152
ui/package.json
152
ui/package.json
|
|
@ -2,12 +2,21 @@
|
|||
"name": "sozsoft-platform-ui",
|
||||
"private": true,
|
||||
"elstarVersion": "2.1.6",
|
||||
"engines": {
|
||||
"node": ">=24.19.0",
|
||||
"npm": ">=11.19.0"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"devextreme:license": "devextreme-license --out src/devextreme-license.ts --force",
|
||||
"prestart": "npm run devextreme:license",
|
||||
"prebuild": "npm run devextreme:license",
|
||||
"prebuild:production": "npm run devextreme:license",
|
||||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"build:production": "vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"prettier": "prettier --ignore-path .gitignore src --check",
|
||||
|
|
@ -15,97 +24,94 @@
|
|||
"format": "npm run prettier:fix && npm run lint:fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.28.3",
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/standalone": "^7.28.0",
|
||||
"@babel/traverse": "^7.28.0",
|
||||
"@babel/types": "^7.28.0",
|
||||
"@dnd-kit/core": "^6.1.0",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@babel/standalone": "^7.29.8",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@floating-ui/react": "^0.27.2",
|
||||
"@fullcalendar/daygrid": "^6.1.8",
|
||||
"@fullcalendar/interaction": "^6.1.8",
|
||||
"@fullcalendar/react": "^6.1.8",
|
||||
"@fullcalendar/timegrid": "^6.1.8",
|
||||
"@marsidev/react-turnstile": "^0.2.1",
|
||||
"@microsoft/signalr": "^9.0.6",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"axios": "^1.18.1",
|
||||
"@floating-ui/react": "^0.27.20",
|
||||
"@fullcalendar/daygrid": "^6.1.21",
|
||||
"@fullcalendar/interaction": "^6.1.21",
|
||||
"@fullcalendar/react": "^6.1.21",
|
||||
"@fullcalendar/timegrid": "^6.1.21",
|
||||
"@marsidev/react-turnstile": "^1.6.0",
|
||||
"@microsoft/signalr": "^10.0.11",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@tanstack/react-query": "^5.101.4",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"axios": "^1.19.0",
|
||||
"classnames": "^2.5.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"devexpress-reporting-react": "^25.2.3",
|
||||
"devextreme": "^25.2.3",
|
||||
"devextreme-exceljs-fork": "^4.4.11",
|
||||
"devextreme-react": "^25.2.3",
|
||||
"easy-peasy": "^6.0.5",
|
||||
"emoji-picker-react": "^4.14.1",
|
||||
"file-saver": "^2.0.2",
|
||||
"formik": "^2.4.6",
|
||||
"framer-motion": "^11.15.0",
|
||||
"dayjs": "^1.11.21",
|
||||
"devexpress-reporting-react": "26.1.4",
|
||||
"devextreme": "26.1.4",
|
||||
"devextreme-exceljs-fork": "^4.4.13",
|
||||
"devextreme-react": "26.1.4",
|
||||
"easy-peasy": "^6.1.1",
|
||||
"emoji-picker-react": "^4.19.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"formik": "^2.4.9",
|
||||
"framer-motion": "^13.1.0",
|
||||
"jspdf": "^4.2.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.18.1",
|
||||
"react": "^18.3.1",
|
||||
"react-advanced-cropper": "^0.20.0",
|
||||
"react-arborist": "^3.4.0",
|
||||
"react-custom-scrollbars-2": "^4.5.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-error-boundary": "^5.0.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-highlight-words": "^0.20.0",
|
||||
"react-icons": "^5.4.0",
|
||||
"react": "^19.2.8",
|
||||
"react-advanced-cropper": "^0.20.1",
|
||||
"react-arborist": "^3.16.0",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-error-boundary": "^6.1.2",
|
||||
"react-highlight-words": "^0.21.0",
|
||||
"react-icons": "^5.7.0",
|
||||
"react-modal": "^3.16.3",
|
||||
"react-router-dom": "^6.30.4",
|
||||
"react-select": "^5.9.0",
|
||||
"yet-another-react-lightbox": "^3.25.0",
|
||||
"yup": "^1.6.1"
|
||||
"react-router-dom": "^7.18.2",
|
||||
"react-select": "^5.10.2",
|
||||
"yet-another-react-lightbox": "^3.32.2",
|
||||
"yup": "^1.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.0",
|
||||
"@rolldown/plugin-babel": "^0.1.7",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/babel__standalone": "^7.1.9",
|
||||
"@eslint/js": "^9.39.5",
|
||||
"@tailwindcss/typography": "^0.5.20",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "^20.19.11",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/react-helmet": "^6.1.9",
|
||||
"@types/react-highlight-words": "^0.16.4",
|
||||
"@types/react-modal": "^3.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.62.1",
|
||||
"@typescript-eslint/parser": "^8.62.1",
|
||||
"@types/lodash": "^4.17.25",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react-dom": "^19.2.4",
|
||||
"@types/react-highlight-words": "^0.20.1",
|
||||
"@types/react-modal": "^3.16.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
||||
"@typescript-eslint/parser": "^8.67.0",
|
||||
"@vitejs/plugin-react": "^6.0.5",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"browserslist": "^4.25.2",
|
||||
"cssnano": "^6.0.1",
|
||||
"eslint": "^9.39.0",
|
||||
"autoprefixer": "^10.5.4",
|
||||
"cssnano": "^8.0.5",
|
||||
"eslint": "^9.39.5",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.1.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.3",
|
||||
"globals": "^16.0.0",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^3.1.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"eslint-plugin-react-refresh": "^0.5.4",
|
||||
"globals": "^17.11.0",
|
||||
"postcss": "^8.5.26",
|
||||
"prettier": "^3.9.6",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"typescript": "5.9",
|
||||
"vite": "^8.2.0",
|
||||
"vite": "^8.2.1",
|
||||
"vite-plugin-pwa": "^1.3.0"
|
||||
},
|
||||
"overrides": {
|
||||
"dompurify": "3.4.11",
|
||||
"glob": "13.0.6"
|
||||
"dompurify": "3.4.13",
|
||||
"glob": "13.0.6",
|
||||
"brace-expansion@1": "1.1.18",
|
||||
"brace-expansion@2": "2.1.4",
|
||||
"brace-expansion@5": "5.0.9",
|
||||
"fast-uri": "3.1.5",
|
||||
"workbox-build": {
|
||||
"source-map": "0.8.0"
|
||||
},
|
||||
"source-map-support": {
|
||||
"source-map": "0.6.1"
|
||||
}
|
||||
},
|
||||
"volta": {
|
||||
"node": "24.13.0",
|
||||
"npm": "11.0.0"
|
||||
"node": "24.19.0",
|
||||
"npm": "11.19.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ function App() {
|
|||
|
||||
return (
|
||||
<StoreProvider store={store}>
|
||||
<BrowserRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
||||
{/* v7_startTransition / v7_relativeSplatPath react-router v7'de varsayılan davranış. */}
|
||||
<BrowserRouter>
|
||||
<DynamicRoutesProvider>
|
||||
<ComponentProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,35 @@
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
/* ScrollBar bileşeninin scroll konteyneri (eski react-custom-scrollbars-2'nin
|
||||
yerini alıyor). Thumb/track görünümü yukarıdaki `*` kurallarından geliyor. */
|
||||
.app-scrollbar {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
/* autoHide: thumb yalnızca hover/odakta görünür, yer kaplaması değişmez. */
|
||||
.app-scrollbar-autohide {
|
||||
scrollbar-color: transparent var(--app-scrollbar-track);
|
||||
transition: scrollbar-color 0.2s ease;
|
||||
}
|
||||
|
||||
.app-scrollbar-autohide:hover,
|
||||
.app-scrollbar-autohide:focus-within {
|
||||
scrollbar-color: var(--app-scrollbar-thumb) var(--app-scrollbar-track);
|
||||
}
|
||||
|
||||
.app-scrollbar-autohide::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.app-scrollbar-autohide:hover::-webkit-scrollbar-thumb,
|
||||
.app-scrollbar-autohide:focus-within::-webkit-scrollbar-thumb {
|
||||
background-color: var(--app-scrollbar-thumb);
|
||||
}
|
||||
|
||||
.dx-scrollable-scrollbar.dx-scrollbar-vertical,
|
||||
.dx-scrollable-scrollbar.dx-scrollbar-vertical .dx-scrollable-scroll {
|
||||
width: var(--app-scrollbar-size) !important;
|
||||
|
|
|
|||
|
|
@ -1,165 +0,0 @@
|
|||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import { FaChevronUp, FaChevronDown, FaBox } from "react-icons/fa";
|
||||
|
||||
export interface Column<T> {
|
||||
key: keyof T | string;
|
||||
header: string;
|
||||
width?: string;
|
||||
sortable?: boolean;
|
||||
render?: (item: T) => React.ReactNode;
|
||||
}
|
||||
|
||||
interface DataTableProps<T> {
|
||||
data: T[];
|
||||
columns: Column<T>[];
|
||||
loading?: boolean;
|
||||
sortBy?: string;
|
||||
sortDirection?: "asc" | "desc";
|
||||
onSort?: (key: string) => void;
|
||||
className?: string;
|
||||
selectable?: boolean; // ✅ ekledik
|
||||
selectedKeys?: string[]; // ✅ ekledik
|
||||
onSelectionChange?: (keys: string[]) => void; // ✅ ekledik
|
||||
}
|
||||
|
||||
function DataTable<T extends { id: string }>({
|
||||
data,
|
||||
columns,
|
||||
loading = false,
|
||||
sortBy,
|
||||
sortDirection,
|
||||
onSort,
|
||||
className,
|
||||
selectable = false,
|
||||
selectedKeys = [],
|
||||
onSelectionChange,
|
||||
}: DataTableProps<T>) {
|
||||
const allSelected = data.length > 0 && selectedKeys.length === data.length;
|
||||
|
||||
const handleSelectAll = (checked: boolean) => {
|
||||
if (!onSelectionChange) return;
|
||||
if (checked) {
|
||||
onSelectionChange(data.map((d) => d.id));
|
||||
} else {
|
||||
onSelectionChange([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectRow = (id: string, checked: boolean) => {
|
||||
if (!onSelectionChange) return;
|
||||
if (checked) {
|
||||
onSelectionChange([...selectedKeys, id]);
|
||||
} else {
|
||||
onSelectionChange(selectedKeys.filter((k) => k !== id));
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
// (loading skeleton senin kodundaki gibi kalabilir)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"bg-white rounded-md shadow-sm border border-gray-200",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
{selectable && (
|
||||
<th className="px-3 py-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={allSelected}
|
||||
onChange={(e) => handleSelectAll(e.target.checked)}
|
||||
/>
|
||||
</th>
|
||||
)}
|
||||
{columns.map((column) => (
|
||||
<th
|
||||
key={String(column.key)}
|
||||
className={classNames(
|
||||
"px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider",
|
||||
column.sortable &&
|
||||
onSort &&
|
||||
"cursor-pointer hover:bg-gray-100 transition-colors",
|
||||
column.width && `w-${column.width}`
|
||||
)}
|
||||
onClick={() =>
|
||||
column.sortable && onSort && onSort(String(column.key))
|
||||
}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{column.header}
|
||||
{column.sortable && onSort && (
|
||||
<div className="ml-1">
|
||||
{sortBy === column.key ? (
|
||||
sortDirection === "asc" ? (
|
||||
<FaChevronUp size={12} className="text-gray-400" />
|
||||
) : (
|
||||
<FaChevronDown
|
||||
size={12}
|
||||
className="text-gray-400"
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<div className="w-3 h-3" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{data.map((item) => (
|
||||
<tr key={item.id} className="hover:bg-gray-50 transition-colors">
|
||||
{selectable && (
|
||||
<td className="px-3 py-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedKeys.includes(item.id)}
|
||||
onChange={(e) =>
|
||||
handleSelectRow(item.id, e.target.checked)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
{columns.map((column) => (
|
||||
<td
|
||||
key={String(column.key)}
|
||||
className="px-3 py-2 whitespace-nowrap text-sm text-gray-900"
|
||||
>
|
||||
{column.render
|
||||
? column.render(item)
|
||||
: String((item as any)[column.key] || "-")}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{data.length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<div className="text-gray-400 mb-3">
|
||||
<FaBox className="mx-auto h-8 w-8" />
|
||||
</div>
|
||||
<h3 className="text-sm font-medium text-gray-900">Veri bulunamadı</h3>
|
||||
<p className="mt-1 text-xs text-gray-500">
|
||||
Kriterlere uygun kayıt bulunmuyor.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataTable;
|
||||
|
|
@ -129,7 +129,7 @@ const Simple = ({ children, content, ...rest }: SimpleProps) => {
|
|||
<div className="text-center">
|
||||
{content}
|
||||
{children
|
||||
? cloneElement(children as ReactElement, {
|
||||
? cloneElement(children as ReactElement<any>, {
|
||||
contentClassName: 'text-center',
|
||||
...rest,
|
||||
})
|
||||
|
|
|
|||
70
ui/src/components/shared/PageTitle.tsx
Normal file
70
ui/src/components/shared/PageTitle.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { useLayoutEffect } from 'react'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
/**
|
||||
* `react-helmet` yerine geçer. O paket 2020'den beri bakımsız ve
|
||||
* `UNSAFE_componentWillMount` kullanıyor.
|
||||
*
|
||||
* React 19 `<title>` etiketini kendisi `<head>`'e taşıyor, ama **dedupe
|
||||
* etmiyor**: aynı anda iki bileşen `<title>` render ederse head'de iki etiket
|
||||
* oluşur ve tarayıcı belge sırasındaki ilkini kullanır. Bu uygulamada
|
||||
* `RouteTitle` (bkz. routes/dynamicRouter.tsx) sayfadan ÖNCE render olup
|
||||
* yalnızca boşluğu doldurmak istiyor; native davranışla yanlış taraf kazanırdı.
|
||||
*
|
||||
* Bu yüzden Helmet'in çözümleme mantığı küçük bir kayıt defterinde korunuyor:
|
||||
* - `fallback` olmayan bir başlık, `fallback` olanı her zaman yener,
|
||||
* - eş önceliktekiler arasında en son kaydedilen kazanır,
|
||||
* - hiçbiri yoksa `APP_NAME`'e düşülür (Helmet'teki `defaultTitle`).
|
||||
*/
|
||||
type Entry = {
|
||||
id: number
|
||||
value: string
|
||||
fallback: boolean
|
||||
}
|
||||
|
||||
const entries: Entry[] = []
|
||||
let nextId = 0
|
||||
|
||||
const resolve = () => {
|
||||
for (let i = entries.length - 1; i >= 0; i--) {
|
||||
if (!entries[i].fallback) return entries[i].value
|
||||
}
|
||||
return entries.length ? entries[entries.length - 1].value : APP_NAME
|
||||
}
|
||||
|
||||
const apply = () => {
|
||||
const next = resolve()
|
||||
if (document.title !== next) document.title = next
|
||||
}
|
||||
|
||||
export interface PageTitleProps {
|
||||
/** Sayfa adı. Boş/undefined ise yalnızca uygulama adı gösterilir. */
|
||||
title?: string | null
|
||||
/**
|
||||
* Helmet'teki `titleTemplate`/`defaultTitle` için uygulama adı.
|
||||
* Varsayılan `APP_NAME`.
|
||||
*/
|
||||
appName?: string
|
||||
/** Yalnızca daha spesifik bir başlık yoksa uygulanır. */
|
||||
fallback?: boolean
|
||||
}
|
||||
|
||||
const PageTitle = ({ title, appName = APP_NAME, fallback = false }: PageTitleProps) => {
|
||||
const value = title ? `${title} | ${appName}` : appName
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const entry: Entry = { id: nextId++, value, fallback }
|
||||
entries.push(entry)
|
||||
apply()
|
||||
|
||||
return () => {
|
||||
const index = entries.indexOf(entry)
|
||||
if (index >= 0) entries.splice(index, 1)
|
||||
apply()
|
||||
}
|
||||
}, [value, fallback])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default PageTitle
|
||||
|
|
@ -24,7 +24,7 @@ const TenantSelector = () => {
|
|||
const { setTenant } = useStoreActions((actions) => actions.auth.tenant)
|
||||
const { setWarning } = useStoreActions((actions) => actions.base.messages)
|
||||
const requestIdRef = useRef(0)
|
||||
const lastRequestedTenantNameRef = useRef<string>()
|
||||
const lastRequestedTenantNameRef = useRef<string | undefined>(undefined)
|
||||
|
||||
const subDomainName = getSubdomain()
|
||||
const isSubdomainTenant = !!subDomainName && subDomainName !== defaultDomain
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import useDarkMode from '@/utils/hooks/useDarkmode'
|
|||
import { useSetting } from '@/utils/hooks/useSetting'
|
||||
import useTabFocus from '@/utils/hooks/useTabFocus'
|
||||
import { ComponentProps, useEffect } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useStylesheet } from '@/utils/hooks/useStylesheet'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { getSetupStatus } from '@/services/setup.service'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
|
|
@ -92,20 +92,13 @@ const Theme = (props: CommonProps) => {
|
|||
...{ locale },
|
||||
} as ConfigProviderValue
|
||||
|
||||
return (
|
||||
<>
|
||||
{setting('App.SiteManagement.Theme.Style') && (
|
||||
<Helmet key="Devexpress stylesheet Helmet">
|
||||
<link
|
||||
key="Devexpress stylesheet Helmet link"
|
||||
rel="stylesheet"
|
||||
href={`/css/${getThemeStyle()}.css`}
|
||||
/>
|
||||
</Helmet>
|
||||
)}
|
||||
<ConfigProvider value={currentTheme}>{props.children}</ConfigProvider>
|
||||
</>
|
||||
// DevExtreme tema CSS'i. Ayar kapalıysa href null olur ve etiket kaldırılır.
|
||||
useStylesheet(
|
||||
setting('App.SiteManagement.Theme.Style') ? `/css/${getThemeStyle()}.css` : null,
|
||||
'devextreme-theme-css',
|
||||
)
|
||||
|
||||
return <ConfigProvider value={currentTheme}>{props.children}</ConfigProvider>
|
||||
}
|
||||
|
||||
export default Theme
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ const Card = forwardRef<HTMLDivElement, CardProps>((props, ref) => {
|
|||
const cardHeaderClass = classNames(
|
||||
'card-header',
|
||||
headerBorder && 'card-header-border',
|
||||
headerExtra && 'card-header-extra',
|
||||
!!headerExtra && 'card-header-extra',
|
||||
headerClass
|
||||
)
|
||||
const cardFooterClass = classNames(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ export interface CheckboxProps extends CommonProps {
|
|||
color?: string
|
||||
defaultChecked?: boolean
|
||||
disabled?: boolean
|
||||
labelRef?: string
|
||||
name?: string
|
||||
onChange?: (values: boolean, e: ChangeEvent<HTMLInputElement>) => void
|
||||
readOnly?: boolean
|
||||
|
|
@ -39,7 +38,6 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
|
|||
defaultChecked,
|
||||
value,
|
||||
checked: controlledChecked,
|
||||
labelRef,
|
||||
field,
|
||||
...rest
|
||||
} = props
|
||||
|
|
@ -137,7 +135,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
|
|||
)
|
||||
|
||||
return (
|
||||
<label ref={labelRef} className={labelClass}>
|
||||
<label className={labelClass}>
|
||||
<input
|
||||
ref={ref}
|
||||
className={checkBoxClass}
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ const Dropdown = forwardRef<HTMLDivElement, DropdownProps>((props, ref) => {
|
|||
...rest
|
||||
} = props
|
||||
|
||||
const overlayTarget = useRef(null)
|
||||
const triggerTarget = useRef(null)
|
||||
const overlayTarget = useRef<HTMLElement>(null)
|
||||
const triggerTarget = useRef<HTMLDivElement>(null)
|
||||
const menuControl = useDropdownMenuContext(overlayTarget)
|
||||
const open = menuControl.open
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const Menu = forwardRef<HTMLElement, DropdownInnerMenuProps>((props, ref) => {
|
|||
const menuRef = useUncertainRef<HTMLElement>(ref)
|
||||
const menuId = useUniqueId('menu-')
|
||||
const menuControl = useDropdownMenuContext<HTMLElement>(
|
||||
menuRef as RefObject<HTMLElement>
|
||||
menuRef as RefObject<HTMLElement | null>
|
||||
)
|
||||
|
||||
const getTransform = (deg: number) => {
|
||||
|
|
|
|||
|
|
@ -64,9 +64,9 @@ const DropdownItem = forwardRef<HTMLElement, DropdownItemProps>(
|
|||
|
||||
const menuitemRef = useUncertainRef<HTMLElement>(
|
||||
ref
|
||||
) as RefObject<HTMLElement>
|
||||
) as RefObject<HTMLElement | null>
|
||||
const menuitemId = useUniqueId('menu-item-')
|
||||
const submenuRef = useRef(null)
|
||||
const submenuRef = useRef<HTMLElement>(null)
|
||||
|
||||
const dropdown = useContext(DropdownContext)
|
||||
const menu = useContext(MenuContext)
|
||||
|
|
@ -185,7 +185,7 @@ const DropdownItem = forwardRef<HTMLElement, DropdownItemProps>(
|
|||
|
||||
return (
|
||||
<DropdownMenuContextProvider value={submenuControl}>
|
||||
{cloneElement(submenu, {
|
||||
{cloneElement(submenu as ReactElement<any>, {
|
||||
ref: submenuRef,
|
||||
hidden: !open,
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const DropdownMenuContextProvider = DropdownMenuContext.Provider
|
|||
export const DropdownMenuContextConsumer = DropdownMenuContext.Consumer
|
||||
|
||||
export function useDropdownMenuContext<E extends HTMLElement>(
|
||||
menuRef: React.RefObject<E>
|
||||
menuRef: React.RefObject<E | null>
|
||||
) {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const Notification = forwardRef<HTMLDivElement, NotificationProps>(
|
|||
<div
|
||||
className={classNames(
|
||||
'notification-title',
|
||||
children && 'mb-1'
|
||||
!!children && 'mb-1'
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ export interface RadioProps
|
|||
color?: string
|
||||
defaultChecked?: boolean
|
||||
disabled?: boolean
|
||||
labelRef?: string
|
||||
name?: string
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onChange?: (value: any, e: MouseEvent) => void
|
||||
|
|
@ -50,7 +49,6 @@ const Radio = forwardRef<HTMLInputElement, RadioProps>((props, ref) => {
|
|||
defaultChecked,
|
||||
disabled = disabledContext,
|
||||
field,
|
||||
labelRef,
|
||||
name = nameContext,
|
||||
onChange,
|
||||
readOnly,
|
||||
|
|
@ -122,7 +120,7 @@ const Radio = forwardRef<HTMLInputElement, RadioProps>((props, ref) => {
|
|||
)
|
||||
|
||||
return (
|
||||
<label ref={labelRef} className={labelClass}>
|
||||
<label className={labelClass}>
|
||||
<input
|
||||
ref={ref}
|
||||
type="radio"
|
||||
|
|
|
|||
|
|
@ -1,100 +1,37 @@
|
|||
import { forwardRef } from 'react'
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2'
|
||||
import type { ScrollbarProps as ReactCustomScrollbarProps } from 'react-custom-scrollbars-2'
|
||||
import classNames from 'classnames'
|
||||
import type { ComponentPropsWithoutRef } from 'react'
|
||||
import type { TypeAttributes } from '../@types/common'
|
||||
|
||||
export interface ScrollbarProps extends ReactCustomScrollbarProps {
|
||||
/**
|
||||
* Eskiden `react-custom-scrollbars-2` sarmalayıcısıydı. O paket React 19'u
|
||||
* desteklemiyor (peer aralığı ^18'de duruyor) ve 2021'den beri bakımsız.
|
||||
*
|
||||
* Yerine düz bir overflow konteyneri kondu: scrollbar görünümü zaten
|
||||
* `src/assets/styles/tailwind/index.css` içinde `*` seçicisine uygulanan
|
||||
* `--app-scrollbar-*` değişkenleriyle tanımlı, yani eski özel `renderThumb*` /
|
||||
* `renderTrack*` fonksiyonlarının ürettiği görünümün aynısı bedavaya geliyor.
|
||||
*/
|
||||
export interface ScrollbarProps extends ComponentPropsWithoutRef<'div'> {
|
||||
direction?: TypeAttributes.Direction
|
||||
/** Scrollbar yalnızca hover/odak sırasında görünsün. */
|
||||
autoHide?: boolean
|
||||
}
|
||||
|
||||
export type ScrollbarRef = Scrollbars
|
||||
export type ScrollbarRef = HTMLDivElement
|
||||
|
||||
const ScrollBar = forwardRef<ScrollbarRef, ScrollbarProps>((props, ref) => {
|
||||
const {
|
||||
direction = 'ltr',
|
||||
renderThumbVertical,
|
||||
renderThumbHorizontal,
|
||||
renderTrackVertical,
|
||||
renderTrackHorizontal,
|
||||
...rest
|
||||
} = props
|
||||
const { autoHide = false, className, direction = 'ltr', children, ...rest } = props
|
||||
|
||||
return (
|
||||
<Scrollbars
|
||||
<div
|
||||
ref={ref}
|
||||
renderView={(props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
...(direction === 'rtl' && {
|
||||
marginLeft: props.style.marginRight,
|
||||
marginRight: 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
renderTrackVertical={
|
||||
renderTrackVertical ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
top: 0,
|
||||
right: direction === 'rtl' ? 'auto' : 0,
|
||||
left: direction === 'rtl' ? 0 : 'auto',
|
||||
bottom: 0,
|
||||
width: 'var(--app-scrollbar-size)',
|
||||
backgroundColor: 'var(--app-scrollbar-track)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
renderTrackHorizontal={
|
||||
renderTrackHorizontal ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
right: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
height: 'var(--app-scrollbar-size)',
|
||||
backgroundColor: 'var(--app-scrollbar-track)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
renderThumbVertical={
|
||||
renderThumbVertical ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
borderRadius: 999,
|
||||
backgroundColor: 'var(--app-scrollbar-thumb)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
renderThumbHorizontal={
|
||||
renderThumbHorizontal ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
borderRadius: 999,
|
||||
backgroundColor: 'var(--app-scrollbar-thumb)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
className={classNames('app-scrollbar', autoHide && 'app-scrollbar-autohide', className)}
|
||||
dir={direction}
|
||||
{...rest}
|
||||
/>
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ const StepItem = forwardRef<HTMLDivElement, StepItemProps>((props, ref) => {
|
|||
|
||||
const stepConnectClass = classNames(
|
||||
'step-connect',
|
||||
title && 'ml-2.5 rtl:mr-2.5',
|
||||
!!title && 'ml-2.5 rtl:mr-2.5',
|
||||
vertical && 'step-connect-vertical',
|
||||
status === COMPLETE ? `bg-${color}` : `inactive`
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export interface SwitcherProps extends CommonProps {
|
|||
defaultChecked?: boolean
|
||||
disabled?: boolean
|
||||
isLoading?: boolean
|
||||
labelRef?: string
|
||||
name?: string
|
||||
onChange?: (checked: boolean, e: ChangeEvent<HTMLInputElement>) => void
|
||||
readOnly?: boolean
|
||||
|
|
@ -30,7 +29,6 @@ const Switcher = forwardRef<HTMLInputElement, SwitcherProps>((props, ref) => {
|
|||
defaultChecked,
|
||||
disabled,
|
||||
isLoading = false,
|
||||
labelRef,
|
||||
name,
|
||||
onChange,
|
||||
readOnly,
|
||||
|
|
@ -102,7 +100,7 @@ const Switcher = forwardRef<HTMLInputElement, SwitcherProps>((props, ref) => {
|
|||
)
|
||||
|
||||
return (
|
||||
<label ref={labelRef} className={switcherClass}>
|
||||
<label className={switcherClass}>
|
||||
<input
|
||||
ref={ref}
|
||||
type="checkbox"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const AmPmInput = forwardRef<HTMLInputElement, AmPmInputProps>((props, ref) => {
|
|||
const { className, onChange, onFocus, value, amLabel, pmLabel, ...rest } =
|
||||
props
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>()
|
||||
const inputRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
|
||||
const handleClick = (event: MouseEvent<HTMLInputElement>) => {
|
||||
event.stopPropagation()
|
||||
|
|
|
|||
|
|
@ -75,10 +75,10 @@ const TimeInput = forwardRef<HTMLInputElement, TimeInputProps>((props, ref) => {
|
|||
|
||||
const uuid = useUniqueId(id)
|
||||
|
||||
const hoursRef = useRef<HTMLInputElement>()
|
||||
const minutesRef = useRef<HTMLInputElement>()
|
||||
const secondsRef = useRef<HTMLInputElement>()
|
||||
const amPmRef = useRef<HTMLInputElement>()
|
||||
const hoursRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
const minutesRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
const secondsRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
const amPmRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
const [time, setTime] = useState(
|
||||
getTimeValues(value || (defaultValue as Date), format, amLabel, pmLabel)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const TimeInputField = forwardRef<HTMLInputElement, TimeInputFieldProps>(
|
|||
|
||||
const [digitsEntered, setDigitsEntered] = useState(0)
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>()
|
||||
const inputRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
|
||||
const handleFocus = (event: FocusEvent<HTMLInputElement>) => {
|
||||
typeof onFocus === 'function' && onFocus(event)
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ const TimeInputRange = forwardRef<HTMLInputElement, TimeInputRangeProps>(
|
|||
|
||||
const uuid = useUniqueId(id)
|
||||
|
||||
const fromTimeRef = useRef<HTMLInputElement>()
|
||||
const toTimeRef = useRef<HTMLInputElement>()
|
||||
const fromTimeRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
const toTimeRef = useRef<HTMLInputElement | undefined>(undefined)
|
||||
const [_value, setValue] = useState(value ?? defaultValue)
|
||||
|
||||
useDidUpdate(() => {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useEffect, useCallback } from 'react'
|
||||
import { findDOMNode } from 'react-dom'
|
||||
import type { RefObject, ReactEventHandler } from 'react'
|
||||
|
||||
type TargetType = RefObject<Element> | Element | null | undefined
|
||||
type TargetType = RefObject<Element | null> | Element | null | undefined
|
||||
|
||||
type Options = {
|
||||
disabled: boolean
|
||||
|
|
@ -35,10 +34,18 @@ const domContains = (context: Element, node: (Node & ParentNode) | null) => {
|
|||
return false
|
||||
}
|
||||
|
||||
const getRefTarget = (ref: RefObject<Element> | Element | null | undefined) => {
|
||||
const getRefTarget = (
|
||||
ref: RefObject<Element | null> | Element | null | undefined
|
||||
) => {
|
||||
return ref && ('current' in ref ? ref.current : ref)
|
||||
}
|
||||
|
||||
/**
|
||||
* React 19 `findDOMNode`'u kaldırdı. Eski fallback yalnızca sınıf bileşeni
|
||||
* örneği verildiğinde devreye giriyordu; React 19'da böyle bir hedef zaten
|
||||
* oluşamıyor (bu hook'a ref ya da DOM elemanı geliyor). DOM düğümü
|
||||
* çözülemezse null dönüp çağıran taraf sessizce atlıyor.
|
||||
*/
|
||||
function getDOMNode(elementOrRef: any) {
|
||||
const element =
|
||||
elementOrRef?.root || elementOrRef?.child || getRefTarget(elementOrRef)
|
||||
|
|
@ -47,8 +54,7 @@ function getDOMNode(elementOrRef: any) {
|
|||
return element
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/no-find-dom-node
|
||||
return findDOMNode(element)
|
||||
return null
|
||||
}
|
||||
|
||||
function isLeftClickEvent(e: MouseEvent) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ function useTimeout(
|
|||
ms = 0,
|
||||
enabled = true
|
||||
): UseTimeoutReturn {
|
||||
const timeout = useRef<ReturnType<typeof setTimeout>>()
|
||||
const timeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
|
||||
const callback = useRef(fn)
|
||||
|
||||
const clear = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import uniqueId from 'lodash/uniqueId'
|
|||
import createUID from '../utils/createUid'
|
||||
|
||||
export default function useUniqueId(prefix = '', len = 10) {
|
||||
const idRef = useRef<string>()
|
||||
const idRef = useRef<string | undefined>(undefined)
|
||||
|
||||
if (!idRef.current) {
|
||||
idRef.current = `${uniqueId(prefix)}-${createUID(len)}`
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// DynamicRouter.tsx
|
||||
import React from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { mapCustomComponentRoutes, mapDynamicRoutes } from './dynamicRouteLoader'
|
||||
import { useDynamicRoutes } from './dynamicRoutesContext'
|
||||
import { useComponents } from '@/contexts/ComponentContext'
|
||||
|
|
@ -19,13 +18,12 @@ const DatabaseSetup = React.lazy(() => import('@/views/setup/DatabaseSetup'))
|
|||
|
||||
/**
|
||||
* Physical views set their own title; this only fills the gap for routes whose
|
||||
* component is compiled at runtime. It renders before the page, so a component
|
||||
* that declares its own Helmet still wins.
|
||||
* component is compiled at runtime. `fallback` sayesinde kendi başlığını
|
||||
* bildiren bir bileşen buradaki başlığı her zaman yener — eskiden bu, Helmet'in
|
||||
* "sonra mount olan kazanır" davranışından geliyordu.
|
||||
*/
|
||||
const RouteTitle = ({ title }: { title?: string }) =>
|
||||
title ? (
|
||||
<Helmet defaultTitle={APP_NAME} title={title} titleTemplate={`%s | ${APP_NAME}`}></Helmet>
|
||||
) : null
|
||||
title ? <PageTitle fallback title={title} /> : null
|
||||
|
||||
const RootRedirect = () => {
|
||||
const location = useLocation()
|
||||
|
|
|
|||
59
ui/src/utils/hooks/useStylesheet.ts
Normal file
59
ui/src/utils/hooks/useStylesheet.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { useLayoutEffect } from 'react'
|
||||
|
||||
/**
|
||||
* `<head>` içinde tek bir `<link rel="stylesheet">` etiketini yönetir.
|
||||
*
|
||||
* Daha önce bu iş `react-helmet`'e veriliyordu. React 19 `<link rel="stylesheet">`
|
||||
* etiketini yalnızca `precedence` verilirse hoist ediyor; o modda da yüklenene
|
||||
* kadar render'ı askıya alıyor ve href değişince eskisini head'de bırakıyor
|
||||
* (yeniden kullanım için, tasarım gereği). Tema CSS'i gibi href'i değişen
|
||||
* durumlarda bu, iki temanın üst üste binmesi demek — bu yüzden etiket `id`
|
||||
* üzerinden elle yönetiliyor.
|
||||
*
|
||||
* Aynı `id`'yi birden fazla bileşen isteyebildiği için referans sayımı var:
|
||||
* etiket ancak son kullanıcı da bıraktığında kaldırılır. (Gantt CSS'i hem ana
|
||||
* görünümden hem alt formdan istenebiliyor.)
|
||||
*
|
||||
* @param href Yüklenecek stylesheet. `null`/`undefined` ise bu bileşen talebini bırakır.
|
||||
* @param id Etiketin sabit DOM id'si; href değişimlerinde aynı etiket güncellenir.
|
||||
*/
|
||||
type Slot = {
|
||||
count: number
|
||||
link: HTMLLinkElement
|
||||
}
|
||||
|
||||
const slots = new Map<string, Slot>()
|
||||
|
||||
export const useStylesheet = (href: string | null | undefined, id: string) => {
|
||||
useLayoutEffect(() => {
|
||||
if (!href) return
|
||||
|
||||
let slot = slots.get(id)
|
||||
if (!slot) {
|
||||
const existing = document.getElementById(id) as HTMLLinkElement | null
|
||||
const link = existing ?? document.createElement('link')
|
||||
if (!existing) {
|
||||
link.id = id
|
||||
link.rel = 'stylesheet'
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
slot = { count: 0, link }
|
||||
slots.set(id, slot)
|
||||
}
|
||||
|
||||
slot.count += 1
|
||||
if (slot.link.getAttribute('href') !== href) slot.link.href = href
|
||||
|
||||
return () => {
|
||||
const current = slots.get(id)
|
||||
if (!current) return
|
||||
current.count -= 1
|
||||
if (current.count <= 0) {
|
||||
current.link.remove()
|
||||
slots.delete(id)
|
||||
}
|
||||
}
|
||||
}, [href, id])
|
||||
}
|
||||
|
||||
export default useStylesheet
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
import Container from '@/components/shared/Container'
|
||||
import DoubleSidedImage from '@/components/shared/DoubleSidedImage'
|
||||
import { Button } from '@/components/ui'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { FaArrowLeft } from 'react-icons/fa';
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
|
|
@ -17,11 +16,7 @@ const AccessDenied = () => {
|
|||
|
||||
return (
|
||||
<Container className="h-full">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Access Denied')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Access Denied')} />
|
||||
<div className="h-full flex flex-col items-center justify-center p-28">
|
||||
<DoubleSidedImage
|
||||
src="/img/others/img-2.png"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import IntranetDashboard from './intranet/Dashboard'
|
||||
|
||||
const Dashboard = () => {
|
||||
|
|
@ -8,11 +7,7 @@ const Dashboard = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Dashboard')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::' + 'Dashboard')} />
|
||||
<IntranetDashboard />
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { Button } from '@/components/ui'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const NotFoundPage = () => {
|
||||
|
|
@ -14,11 +13,7 @@ const NotFoundPage = () => {
|
|||
|
||||
return (
|
||||
<div className="p-28">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Not Found')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Not Found')} />
|
||||
<div className="flex items-center justify-center font-inter">
|
||||
<div className="text-[8rem] sm:text-[10rem] md:text-[12rem] font-bold bg-gradient-to-br from-primary to-secondary bg-clip-text animate-pulse">
|
||||
404
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { Button } from '@/components/ui'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
|
|
@ -35,7 +35,7 @@ const ServiceUnavailable = () => {
|
|||
isDark ? 'bg-gray-900 text-gray-100' : 'bg-gray-100 text-gray-900',
|
||||
)}
|
||||
>
|
||||
<Helmet title={`${APP_NAME} — Service unavailable`} />
|
||||
<PageTitle title="Service unavailable" />
|
||||
|
||||
<div
|
||||
className={classNames(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Alert, Button } from '@/components/ui'
|
|||
import { store, useStoreState } from '@/store'
|
||||
import { Suspense, useEffect } from 'react'
|
||||
import { ErrorBoundary } from 'react-error-boundary'
|
||||
import type { FallbackProps } from 'react-error-boundary'
|
||||
import { FaArrowLeft } from 'react-icons/fa'
|
||||
import { Navigate, useLocation } from 'react-router-dom'
|
||||
import DialogProvider from './shared/DialogContext'
|
||||
|
|
@ -17,11 +18,16 @@ interface ViewsProps {
|
|||
layout?: LayoutType
|
||||
}
|
||||
|
||||
function fallbackRender({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: any }) {
|
||||
// react-error-boundary v6'dan itibaren `error` `unknown` tipinde geliyor;
|
||||
// throw edilen her şey Error olmayabilir, o yüzden daraltıyoruz.
|
||||
function fallbackRender({ error, resetErrorBoundary }: FallbackProps) {
|
||||
const { name, message } =
|
||||
error instanceof Error ? error : { name: 'Hata!', message: String(error) }
|
||||
|
||||
return (
|
||||
<Alert showIcon className="mb-4" type="danger">
|
||||
<h5>{error.name ?? 'Hata!'}</h5>
|
||||
<div>{error.message}</div>
|
||||
<h5>{name ?? 'Hata!'}</h5>
|
||||
<div>{message}</div>
|
||||
<Button
|
||||
icon={<FaArrowLeft />}
|
||||
size="sm"
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ import groupBy from 'lodash/groupBy'
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import Log from './components/Log'
|
||||
import LogFilter from './components/LogFilter'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { DIR_RTL } from '@/constants/theme.constant'
|
||||
import { useStoreState } from '@/store'
|
||||
|
||||
|
|
@ -86,11 +85,7 @@ const ActivityLog = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Abp.Identity.ActivityLogs')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Abp.Identity.ActivityLogs')} />
|
||||
|
||||
<AdaptableCard className="overflow-hidden bg-white dark:bg-gray-800">
|
||||
<div className="w-full">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useEffect, useCallback, useMemo, useRef, type MouseEvent } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { Button, Input, Select, toast, Notification, Spinner } from '@/components/ui'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
import {
|
||||
|
|
@ -39,7 +39,6 @@ import type {
|
|||
FileManagerFilters,
|
||||
} from '@/types/fileManagement'
|
||||
import classNames from 'classnames'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
const { VITE_CDN_URL } = import.meta.env
|
||||
|
||||
|
|
@ -800,11 +799,7 @@ const FileManager = () => {
|
|||
|
||||
return (
|
||||
<Container className="px-1">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Files')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.Files')} />
|
||||
|
||||
{/* Toolbar */}
|
||||
<div className="dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-2 mb-4 mt-2">
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
FaUndo,
|
||||
FaFileImage,
|
||||
} from 'react-icons/fa'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
import {
|
||||
getOrgChartDepartments,
|
||||
|
|
@ -30,7 +30,7 @@ import {
|
|||
OrgChartUserDto,
|
||||
} from '@/services/orgChart.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { AVATAR_URL, APP_NAME } from '@/constants/app.constant'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
import Loading from '@/components/shared/Loading'
|
||||
import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
|
||||
import { Avatar, Button } from '@/components/ui'
|
||||
|
|
@ -655,7 +655,7 @@ function OrgChartTree({
|
|||
const nodeRefs = useRef<NodeRefMap>({})
|
||||
const cardRefs = useRef<CardRefMap>({})
|
||||
const [paths, setPaths] = useState<string[]>([])
|
||||
const repaintRafRef = useRef<number>()
|
||||
const repaintRafRef = useRef<number | undefined>(undefined)
|
||||
|
||||
const recomputePaths = useCallback(() => {
|
||||
const container = containerRef.current
|
||||
|
|
@ -874,9 +874,7 @@ const OrgChart = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{APP_NAME} — Organizasyon Şeması</title>
|
||||
</Helmet>
|
||||
<PageTitle title="Organizasyon Şeması" />
|
||||
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Toolbar */}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useStoreActions, useStoreState } from '@/store'
|
|||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { FormikHelpers } from 'formik'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import FormCustomization from './FormCustomization'
|
||||
import FormTabColumns from './FormTabColumns'
|
||||
|
|
@ -59,7 +59,6 @@ import FormTabRow from './FormTabRow'
|
|||
import FormTabGantt from './FormTabGantt'
|
||||
import FormTabScheduler from './FormTabScheduler'
|
||||
import FormTabTodo from './FormTabTodo'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { FormTabWorkflow } from './FormTabWorkflow'
|
||||
|
||||
export interface FormEditProps {
|
||||
|
|
@ -228,11 +227,7 @@ const FormEdit = () => {
|
|||
|
||||
return listFormCode && listFormValues && customizations && roleList && userList ? (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={`${listFormCode} - ${translate(`::${listFormValues.title}`)}`}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={`${listFormCode} - ${translate(`::${listFormValues.title}`)}`} />
|
||||
<div className="flex items-center justify-between mb-2 gap-3">
|
||||
{/* SOL TARAF */}
|
||||
<h4 className="mb-0">
|
||||
|
|
|
|||
|
|
@ -219,7 +219,6 @@ export const columnEditorTypeListOptions = [
|
|||
export const listFormTypeOptions = [
|
||||
{ value: 'Form', label: 'Form' },
|
||||
{ value: 'List', label: 'List' },
|
||||
{ value: 'Chart', label: 'Chart' },
|
||||
]
|
||||
|
||||
export const listFormDefaultLayoutOptions = [
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
import { Form, Formik, FormikProps } from 'formik'
|
||||
import type { KeyboardEvent } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import * as Yup from 'yup'
|
||||
import { FaFolderOpen } from 'react-icons/fa'
|
||||
|
|
@ -14,7 +14,6 @@ import { getPermissions } from '@/services/identity.service'
|
|||
import { getDataSources } from '@/services/data-source.service'
|
||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||
import type { SqlObjectExplorerDto, DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { MenuItem } from '@/proxy/menus/menu'
|
||||
import WizardStep1, {
|
||||
MenuTreeNode,
|
||||
|
|
@ -862,11 +861,7 @@ const Wizard = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + (isEditMode ? 'App.Listforms.WizardEdit' : 'App.Listforms.Wizard'))}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::' + (isEditMode ? 'App.Listforms.WizardEdit' : 'App.Listforms.Wizard'))} />
|
||||
|
||||
{isLoadingEditData && (
|
||||
<p className="text-xs text-gray-400 text-center py-4 animate-pulse">
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export function WorkflowCanvas({
|
|||
activeNodeId?: string
|
||||
selectedId: string
|
||||
pendingLink: PendingLink
|
||||
canvasRef: RefObject<HTMLDivElement>
|
||||
canvasRef: RefObject<HTMLDivElement | null>
|
||||
onSelect: (id: string) => void
|
||||
onOpenDetails: (id: string) => void
|
||||
onClearSelection: () => void
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
|||
|
||||
type WorkflowDesignerProps = {
|
||||
busy: boolean
|
||||
canvasRef: RefObject<HTMLDivElement>
|
||||
canvasRef: RefObject<HTMLDivElement | null>
|
||||
canvasZoom: number
|
||||
criteriaForm: any
|
||||
currentCriteria: WorkflowCriteriaDto[]
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import isEmpty from 'lodash/isEmpty'
|
|||
import { memo, Suspense, useEffect, useMemo, useState } from 'react'
|
||||
import { NodeApi, NodeRendererProps } from 'react-arborist'
|
||||
import { Tree } from 'react-arborist/dist/module/components/tree'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import {
|
||||
FaCheckCircle,
|
||||
FaUser,
|
||||
|
|
@ -67,7 +67,6 @@ import {
|
|||
} from 'react-icons/fa'
|
||||
import { object, string } from 'yup'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
const schema = object().shape({
|
||||
id: string(),
|
||||
|
|
@ -463,11 +462,7 @@ const OrganizationUnits = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::Abp.Identity.OrganizationUnits')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::Abp.Identity.OrganizationUnits')} />
|
||||
<Loading type="cover" className="h-full" loading={loading}>
|
||||
<Container className="h-full">
|
||||
<div className="flex flex-col lg:flex-row gap-4 h-full">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import AdaptableCard from '@/components/shared/AdaptableCard'
|
||||
import Container from '@/components/shared/Container'
|
||||
import Tabs from '@/components/ui/Tabs'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Suspense, lazy, type ReactNode } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { FaCheckCircle, FaCompressAlt, FaUser } from 'react-icons/fa'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
|
|
@ -56,11 +55,7 @@ const Profile = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Abp.Identity.Profile')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Abp.Identity.Profile')} />
|
||||
<AdaptableCard>
|
||||
<Tabs value={currentTab} onChange={(val) => onTabChange(val)}>
|
||||
<TabList>
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
import useThemeClass from '@/utils/hooks/useThemeClass'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import RolesPermission from './RolesPermission'
|
||||
import { IdentityRoleDto } from '@/proxy/admin/models'
|
||||
import { getRoles } from '@/services/identity.service'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
const { Tr, Th, Td, THead, TBody } = Table
|
||||
|
||||
|
|
@ -37,11 +36,7 @@ const Roles = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::AbpIdentity.Roles')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::AbpIdentity.Roles')} />
|
||||
<Button
|
||||
onClick={() => {
|
||||
toast.push(
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
import useThemeClass from '@/utils/hooks/useThemeClass'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import TenantsConnectionString from './TenantsConnectionString'
|
||||
import { getTenants } from '@/services/tenant.service'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
const { Tr, Th, Td, THead, TBody } = Table
|
||||
|
||||
|
|
@ -43,11 +42,7 @@ const Tenants = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::AbpTenantManagement.Tenants')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::AbpTenantManagement.Tenants')} />
|
||||
<Loading type="cover" loading={loading}>
|
||||
<Container>
|
||||
<AdaptableCard>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
import dayjs from 'dayjs'
|
||||
import { Field, FieldArray, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import {
|
||||
FaBuilding,
|
||||
FaLockOpen,
|
||||
|
|
@ -83,7 +83,7 @@ import {
|
|||
} from '@/types/shared'
|
||||
import { AdaptableCard, ConfirmDialog } from '@/components/shared'
|
||||
import { AssignedClaimViewModel, UserInfoViewModel } from '@/proxy/admin/models'
|
||||
import { APP_NAME, AVATAR_URL } from '@/constants/app.constant'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
import { useSetting } from '@/utils/hooks/useSetting'
|
||||
|
||||
|
|
@ -230,11 +230,7 @@ function UserDetails() {
|
|||
|
||||
return userDetails ? (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={userDetails.email}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={userDetails.email} />
|
||||
<AdaptableCard>
|
||||
<Tabs defaultValue="user">
|
||||
<TabList>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
import useThemeClass from '@/utils/hooks/useThemeClass'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import UsersPermission from './UsersPermission'
|
||||
import { IdentityUserDto } from '@/proxy/admin/models'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import ActionLink from '@/components/shared/ActionLink'
|
||||
|
||||
const { Tr, Th, Td, THead, TBody } = Table
|
||||
|
|
@ -44,11 +43,7 @@ const Users = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::AbpIdentity.Users')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::AbpIdentity.Users')} />
|
||||
<Container>
|
||||
<AdaptableCard>
|
||||
<Table>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { FaEye, FaUsers, FaCrown } from 'react-icons/fa'
|
|||
import { useStoreActions, useStoreState } from '@/store/store'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Role } from '@/proxy/videoroom/models'
|
||||
|
||||
|
|
@ -24,11 +24,7 @@ const Dashboard = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate="%s | Erp Platform"
|
||||
title={translate('::' + 'App.Videoroom.Dashboard')}
|
||||
defaultTitle="Erp Platform"
|
||||
></Helmet>
|
||||
<PageTitle appName="Erp Platform" title={translate('::' + 'App.Videoroom.Dashboard')} />
|
||||
<div className="flex items-center justify-center sm:mt-2 lg:mt-24 dark:bg-gray-900">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import {
|
|||
import { showDbDateAsIs } from '@/utils/dateUtils'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import ChatPanel from '@/views/admin/videoroom/ChatPanel'
|
||||
import ParticipantsPanel from '@/views/admin/videoroom/ParticipantsPanel'
|
||||
|
|
@ -132,8 +132,8 @@ const RoomDetail = () => {
|
|||
autoMuteNewParticipants: true,
|
||||
})
|
||||
|
||||
const signalRServiceRef = useRef<SignalRService>()
|
||||
const webRTCServiceRef = useRef<WebRTCService>()
|
||||
const signalRServiceRef = useRef<SignalRService | undefined>(undefined)
|
||||
const webRTCServiceRef = useRef<WebRTCService | undefined>(undefined)
|
||||
const [teacherDisconnected, setTeacherDisconnected] = useState(false)
|
||||
|
||||
const layouts: VideoroomLayoutDto[] = [
|
||||
|
|
@ -849,11 +849,7 @@ const RoomDetail = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate="%s | Erp Platform"
|
||||
title={translate('::' + 'App.Videoroom.RoomDetail')}
|
||||
defaultTitle="Erp Platform"
|
||||
></Helmet>
|
||||
<PageTitle appName="Erp Platform" title={translate('::' + 'App.Videoroom.RoomDetail')} />
|
||||
|
||||
{teacherDisconnected && (
|
||||
<div className="bg-red-600 text-white p-2 text-center">
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import {
|
|||
startVideoroom,
|
||||
updateVideoroom,
|
||||
} from '@/services/videoroom.service'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { VideoroomDto } from '@/proxy/videoroom/models'
|
||||
import classNames from 'classnames'
|
||||
|
|
@ -253,11 +253,7 @@ const RoomList = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate="%s | Erp Platform"
|
||||
title={translate('::' + 'App.Videoroom.List')}
|
||||
defaultTitle="Erp Platform"
|
||||
></Helmet>
|
||||
<PageTitle appName="Erp Platform" title={translate('::' + 'App.Videoroom.List')} />
|
||||
|
||||
<Container>
|
||||
{/* ── Header ─────────────────────────────────────────────── */}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { FormEvent, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { FaComment, FaPaperPlane, FaPlus, FaRobot } from 'react-icons/fa'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useStoreState } from '@/store'
|
||||
import { Button, Drawer, Dropdown, ScrollBar, Spinner } from '@/components/ui'
|
||||
import { Container } from '@/components/shared'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { aiService } from '@/services/ai.service'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { AI_ASSISTANT } from '@/constants/permission.constant'
|
||||
import ConversationList from './components/ConversationList'
|
||||
import MessageBubble from './components/MessageBubble'
|
||||
|
|
@ -143,11 +142,7 @@ const Assistant = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + AI_ASSISTANT)}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::' + AI_ASSISTANT)} />
|
||||
|
||||
<Drawer
|
||||
isOpen={isConversationDrawerOpen}
|
||||
|
|
@ -199,23 +194,10 @@ const Assistant = () => {
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
<ScrollBar
|
||||
autoHide
|
||||
hideTracksWhenNotNeeded
|
||||
autoHideTimeout={700}
|
||||
autoHideDuration={200}
|
||||
className="flex-1 min-h-0 min-w-0"
|
||||
renderView={({ style, ...props }) => (
|
||||
<div {...props} style={{ ...style, overflowX: 'hidden' }} />
|
||||
)}
|
||||
renderThumbVertical={({ style, ...props }) => (
|
||||
<div
|
||||
{...props}
|
||||
className="rounded-full bg-gray-300/80 dark:bg-gray-600/80"
|
||||
style={{ ...style, width: 6, right: 3 }}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{/* Eski renderView/renderThumbVertical prop'ları react-custom-scrollbars-2'ye
|
||||
aitti; yatay taşma artık utility sınıfıyla, thumb görünümü ise global
|
||||
--app-scrollbar-* stilleriyle sağlanıyor. */}
|
||||
<ScrollBar autoHide className="flex-1 min-h-0 min-w-0 overflow-x-hidden">
|
||||
<div className="p-4 space-y-4">
|
||||
{messages.length === 0 && (
|
||||
<div className="text-center text-gray-500 dark:text-gray-400 mt-8">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import Alert from '@/components/ui/Alert'
|
|||
import Button from '@/components/ui/Button'
|
||||
import { FormContainer, FormItem } from '@/components/ui/Form'
|
||||
import Input from '@/components/ui/Input'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { sendExtendLoginRequest } from '@/services/account.service'
|
||||
import { store, useStoreActions } from '@/store'
|
||||
|
|
@ -14,7 +13,7 @@ import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage'
|
|||
import { TurnstileInstance } from '@marsidev/react-turnstile'
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
type ExtendLoginFormSchema = {
|
||||
|
|
@ -76,11 +75,7 @@ const ExtendLogin = () => {
|
|||
|
||||
return emailSent ? (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('AbpAccount::' + 'Abp.Account.ExtendLogin')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('AbpAccount::' + 'Abp.Account.ExtendLogin')} />
|
||||
<div>
|
||||
<h3 className="mb-1">{translate('::Abp.Account.ExtendLogin.Title')}</h3>
|
||||
<Alert showIcon className="mt-4 mb-4" type="success">
|
||||
|
|
@ -94,11 +89,7 @@ const ExtendLogin = () => {
|
|||
</>
|
||||
) : (
|
||||
<div>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Abp.Account.ExtendLogin')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Abp.Account.ExtendLogin')} />
|
||||
<div className="mb-6">
|
||||
<h3 className="mb-1">{translate('::Abp.Account.ExtendLogin')}</h3>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import Alert from '@/components/ui/Alert'
|
|||
import Button from '@/components/ui/Button'
|
||||
import { FormContainer, FormItem } from '@/components/ui/Form'
|
||||
import Input from '@/components/ui/Input'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { sendPasswordResetCode } from '@/services/account.service'
|
||||
import { store } from '@/store'
|
||||
|
|
@ -16,7 +15,7 @@ import type { AxiosError } from 'axios'
|
|||
import { Field, Form, Formik } from 'formik'
|
||||
import { motion } from 'framer-motion'
|
||||
import { useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
type ForgotPasswordFormSchema = {
|
||||
|
|
@ -63,15 +62,11 @@ const ForgotPassword = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Abp.Account.ForgotPassword')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Abp.Account.ForgotPassword')} />
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 100 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, origin: 1 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="mb-6">
|
||||
{emailSent ? (
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ import { motion } from 'framer-motion'
|
|||
import { useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import * as Yup from 'yup'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useSetting } from '@/utils/hooks/useSetting'
|
||||
|
||||
type SignInFormSchema = {
|
||||
|
|
@ -161,15 +160,11 @@ const Login = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('AbpAccount::' + 'Login')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('AbpAccount::' + 'Login')} />
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 100 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, origin: 1 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="mb-8 relative text-center">
|
||||
<h3 className="mb-1 m-5">{translate('::Abp.Account.WelcomeBack')}</h3>
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ import { Field, Form, Formik } from 'formik'
|
|||
import { useRef, useState } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
|
||||
type SignUpFormSchema = {
|
||||
password: string
|
||||
|
|
@ -71,11 +70,7 @@ const Register = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('AbpAccount::' + 'Register')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('AbpAccount::' + 'Register')} />
|
||||
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-1">{translate('::Abp.Account.SignUp')}</h3>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import PasswordInput from '@/components/shared/PasswordInput'
|
|||
import Alert from '@/components/ui/Alert'
|
||||
import Button from '@/components/ui/Button'
|
||||
import { FormContainer, FormItem } from '@/components/ui/Form'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { resetPassword } from '@/services/account.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
|
|
@ -11,7 +10,7 @@ import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage'
|
|||
import type { AxiosError } from 'axios'
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import { useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
|
|
@ -77,11 +76,7 @@ const ResetPassword = () => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('AbpAccount::' + 'ResetPassword')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('AbpAccount::' + 'ResetPassword')} />
|
||||
<div className="mb-6">
|
||||
{resetComplete ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import { ROUTES_ENUM } from '@/routes/route.constant'
|
|||
import { store, useStoreActions } from '@/store'
|
||||
import Captcha from '@/components/shared/Captcha'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { TurnstileInstance } from '@marsidev/react-turnstile'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import ActionLink from '@/components/shared/ActionLink'
|
||||
|
|
@ -58,11 +57,7 @@ const SendConfirmationCode = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Abp.Account.SendConfirmationCode')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Abp.Account.SendConfirmationCode')} />
|
||||
|
||||
{!message && (
|
||||
<div className="mb-8">
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import { Alert, Button } from '@/components/ui'
|
|||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import useAccount from '@/utils/hooks/useAccount'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
const VerifyConfirmationCode = () => {
|
||||
const { userId, token } = useParams()
|
||||
|
|
@ -22,11 +21,7 @@ const VerifyConfirmationCode = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Abp.Account.VerifyConfirmationCode')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Abp.Account.VerifyConfirmationCode')} />
|
||||
|
||||
<div>
|
||||
{message && (
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ import Widget from '@/components/common/Widget'
|
|||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Loading } from '../../components/shared'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { Button } from '@/components/ui'
|
||||
import Dialog from '@/components/ui/Dialog'
|
||||
import ComponentEditor from './ComponentEditor'
|
||||
|
|
@ -93,11 +92,7 @@ const ComponentManager: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.DeveloperKit.Components')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'App.DeveloperKit.Components')} />
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-2 mb-4">
|
||||
<Widget
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ import { developerKitService } from '@/services/developerKit.service'
|
|||
import type { DataSourceDto } from '@/proxy/data-source'
|
||||
import type { DatabaseTableDto } from '@/proxy/sql-query-manager/models'
|
||||
import type { CrudEndpoint } from '@/proxy/developerKit/models'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import Button from '@/components/ui/Button'
|
||||
|
||||
interface TestResult {
|
||||
|
|
@ -317,11 +316,7 @@ const CrudEndpointManager: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-4 bg-gray-50 dark:bg-gray-950">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.DeveloperKit.CrudEndpoints')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'App.DeveloperKit.CrudEndpoints')} />
|
||||
|
||||
{/* Stats Row */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mt-2">
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ import {
|
|||
postTestCompile,
|
||||
type TestCompileDto,
|
||||
} from '@/services/dynamicService.service'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import Button from '@/components/ui/Button'
|
||||
|
||||
|
|
@ -195,7 +194,7 @@ const DynamicServiceEditor: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Helmet titleTemplate={`%s | ${APP_NAME}`} title={pageTitle} defaultTitle={APP_NAME} />
|
||||
<PageTitle title={pageTitle} />
|
||||
|
||||
{/* Header */}
|
||||
<div className="bg-white dark:bg-gray-900 shadow-lg border-b border-slate-200 dark:border-gray-700 sticky top-0 z-10">
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ import {
|
|||
import Widget from '@/components/common/Widget'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { dynamicServiceService, type DynamicServiceDto } from '@/services/dynamicService.service'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import Button from '@/components/ui/Button'
|
||||
|
||||
|
|
@ -79,11 +78,7 @@ const DynamicServiceManager: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.DeveloperKit.DynamicServices')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::' + 'App.DeveloperKit.DynamicServices')} />
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-5 gap-4 mt-2 mb-4">
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
import SqlObjectExplorer, { type SqlExplorerSelectedObject } from './SqlObjectExplorer'
|
||||
import SqlEditor, { SqlEditorRef } from './SqlEditor'
|
||||
import { Splitter } from '@/components/codeLayout/Splitter'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useStoreState } from '@/store/store'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import DbMigrateButton from '@/components/shared/DbMigrateButton'
|
||||
|
||||
const SqlResultsGrid = lazy(() => import('./SqlResultsGrid'))
|
||||
|
|
@ -934,11 +933,7 @@ GO`,
|
|||
className="flex flex-col overflow-x-hidden overflow-y-auto lg:overflow-hidden"
|
||||
style={{ height: 'calc(100vh - 130px)' }}
|
||||
>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.SqlQueryManager')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'App.SqlQueryManager')} />
|
||||
|
||||
<div className="flex flex-col flex-1 min-h-0 p-1">
|
||||
{/* Toolbar */}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Editor from '@monaco-editor/react'
|
||||
import axios from 'axios'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import {
|
||||
FaArrowLeft,
|
||||
|
|
@ -33,7 +33,6 @@ import {
|
|||
} from 'react-icons/fa'
|
||||
import { useComponents } from '@/contexts/ComponentContext'
|
||||
import type { CrudEndpoint, CustomComponent } from '@/proxy/developerKit/models'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import apiService from '@/services/api.service'
|
||||
import { developerKitService } from '@/services/developerKit.service'
|
||||
|
|
@ -3684,7 +3683,7 @@ const VisualComponentDesigner = () => {
|
|||
))
|
||||
|
||||
const pageHelmet = (
|
||||
<Helmet defaultTitle={APP_NAME} title={pageTitle} titleTemplate={`%s | ${APP_NAME}`}></Helmet>
|
||||
<PageTitle title={pageTitle} />
|
||||
)
|
||||
|
||||
if (!component) {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const FormDevExpress = (props: {
|
|||
listFormCode: string
|
||||
isSubForm?: boolean
|
||||
mode: RowMode
|
||||
refForm: RefObject<FormRef>
|
||||
refForm: RefObject<FormRef | null>
|
||||
formData: any
|
||||
formItems: GroupItem[]
|
||||
setFormData: Dispatch<any>
|
||||
|
|
@ -46,8 +46,8 @@ const FormDevExpress = (props: {
|
|||
|
||||
const formDataRef = useRef(formData)
|
||||
const formItemsRef = useRef(formItems)
|
||||
const formInstanceRef = useRef<any>()
|
||||
const lastContentReadyScriptKeyRef = useRef<string>()
|
||||
const formInstanceRef = useRef<any | undefined>(undefined)
|
||||
const lastContentReadyScriptKeyRef = useRef<string | undefined>(undefined)
|
||||
const didAutoFocusRef = useRef(false)
|
||||
const [runtimeReadOnlyFields, setRuntimeReadOnlyFields] = useState<Record<string, boolean>>({})
|
||||
const runtimeReadOnlyFieldsRef = useRef<Record<string, boolean>>({})
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const FormNewContent = (props: FormProps = { isSubForm: false }) => {
|
|||
const params = useParams()
|
||||
const listFormCode = props?.listFormCode ?? params?.listFormCode ?? ''
|
||||
const [searchParams] = useSearchParams()
|
||||
const initializedListFormCodeRef = useRef<string>()
|
||||
const initializedListFormCodeRef = useRef<string | undefined>(undefined)
|
||||
|
||||
const {
|
||||
setFormData,
|
||||
|
|
|
|||
|
|
@ -8,14 +8,13 @@ import type { ReactNode, RefObject } from 'react'
|
|||
import { useState } from 'react'
|
||||
import { Container, Loading } from '@/components/shared'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import type { GridDto } from '@/proxy/form/models'
|
||||
import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { usePermission } from '@/utils/hooks/usePermission'
|
||||
import type { FormRef } from 'devextreme-react/form'
|
||||
import type { GroupItem } from 'devextreme/ui/form'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import FormDevExpress from '../FormDevExpress'
|
||||
import SubForms from '../SubForms'
|
||||
import { NotePanel } from '../notes/NotePanel'
|
||||
|
|
@ -30,7 +29,7 @@ type FormPageShellProps = {
|
|||
gridDto?: GridDto
|
||||
formData: any
|
||||
formItems: GroupItem[]
|
||||
refForm: RefObject<FormRef>
|
||||
refForm: RefObject<FormRef | null>
|
||||
setFormData: (data: any) => void
|
||||
/** Başlığın sağındaki aksiyon butonları (FormButtons). */
|
||||
actions?: ReactNode
|
||||
|
|
@ -96,7 +95,7 @@ export const FormPageShell = ({
|
|||
} transition-all duration-300`}
|
||||
>
|
||||
{!isSubForm && (
|
||||
<Helmet titleTemplate={`%s | ${APP_NAME}`} title={title} defaultTitle={APP_NAME} />
|
||||
<PageTitle title={title} />
|
||||
)}
|
||||
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ const useGridData = (props: {
|
|||
const [permissionResults, setPermissionResults] = useState<PermissionResults>()
|
||||
|
||||
const refForm = useRef<FormRef>(null)
|
||||
const previousFormDataRef = useRef<any>()
|
||||
const previousFormDataRef = useRef<any | undefined>(undefined)
|
||||
const [searchParams] = useSearchParams()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import { useState, useEffect } from 'react'
|
|||
import { useForumData } from './useForumData'
|
||||
import { ForumView } from './forum/ForumView'
|
||||
import { Container } from '@/components/shared'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { Button } from '@/components/ui'
|
||||
|
||||
export function Forum() {
|
||||
|
|
@ -51,11 +50,7 @@ export function Forum() {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.Forum')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'App.Forum')} />
|
||||
|
||||
{error && (
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ import { useEffect } from 'react'
|
|||
import { useForumData } from './useForumData'
|
||||
import { AdminView } from './admin/AdminView'
|
||||
import { Container } from '@/components/shared'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { Button } from '@/components/ui'
|
||||
|
||||
export function Management() {
|
||||
|
|
@ -60,11 +59,7 @@ export function Management() {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.ForumManagement')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'App.ForumManagement')} />
|
||||
|
||||
{error && (
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Container from '@/components/shared/Container'
|
|||
import { Loading } from '@/components/shared'
|
||||
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
|
||||
import { Dialog, Notification, toast } from '@/components/ui'
|
||||
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import {
|
||||
ColumnFormatDto,
|
||||
DbTypeEnum,
|
||||
|
|
@ -36,7 +36,7 @@ import CustomStore from 'devextreme/data/custom_store'
|
|||
import type { DataType } from 'devextreme/common'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { NotePanel } from '../form/notes/NotePanel'
|
||||
import SubForms from '../form/SubForms'
|
||||
import GridFilterDialogs from './GridFilterDialogs'
|
||||
|
|
@ -359,10 +359,10 @@ const CardView = (props: CardViewProps) => {
|
|||
)
|
||||
const localizationTexts = useStoreState((state) => state.abpConfig.texts)
|
||||
const useMobileEditPopup = shouldUseMobileEditPopup()
|
||||
const cardViewRef = useRef<CardViewRef>()
|
||||
const cardViewRef = useRef<CardViewRef | undefined>(undefined)
|
||||
const searchParamsRef = useRef(searchParams)
|
||||
const defaultSearchFilterRef = useRef(searchParams?.get('filter') ?? null)
|
||||
const dataSourceSearchParamsRef = useRef<URLSearchParams>()
|
||||
const dataSourceSearchParamsRef = useRef<URLSearchParams | undefined>(undefined)
|
||||
const extraFilterMountedRef = useRef(false)
|
||||
const [gridDtoState, setGridDtoState] = useState<{
|
||||
listFormCode: string
|
||||
|
|
@ -376,7 +376,7 @@ const CardView = (props: CardViewProps) => {
|
|||
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
|
||||
const [formData, setFormData] = useState<Record<string, any>>()
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const formDataRef = useRef<Record<string, any>>()
|
||||
const formDataRef = useRef<Record<string, any> | undefined>(undefined)
|
||||
useEffect(() => {
|
||||
formDataRef.current = formData
|
||||
}, [formData])
|
||||
|
|
@ -647,7 +647,7 @@ const CardView = (props: CardViewProps) => {
|
|||
// Grid/Tree/Scheduler ile aynı editorScript altyapısı; CardView'ın New ve Edit
|
||||
// popup formlarında da script'ler çalışsın diye bağlanır.
|
||||
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||
const editingFormInstanceRef = useRef<any>()
|
||||
const editingFormInstanceRef = useRef<any | undefined>(undefined)
|
||||
const editorScriptHandlers = useMemo(
|
||||
() =>
|
||||
createEditingFormHandlers({
|
||||
|
|
@ -1109,11 +1109,7 @@ const CardView = (props: CardViewProps) => {
|
|||
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
{!isSubForm && (
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + gridDto?.gridOptions.title)}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::' + gridDto?.gridOptions.title)} />
|
||||
)}
|
||||
{!gridDto ||
|
||||
!dataSource ||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { CommonProps } from '@/proxy/common'
|
||||
import { Meta } from '@/proxy/routes/routes'
|
||||
import { Container } from '@/components/shared'
|
||||
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import DxChart from 'devextreme-react/chart'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useParams, useSearchParams } from 'react-router-dom'
|
||||
import { GridDto } from '@/proxy/form/models'
|
||||
import { usePermission } from '@/utils/hooks/usePermission'
|
||||
|
|
@ -357,11 +357,7 @@ const Chart = (props: ChartProps) => {
|
|||
return (
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
{!isSubForm && gridDto && (
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + gridDto?.gridOptions?.title)}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + gridDto?.gridOptions?.title)} />
|
||||
)}
|
||||
{_listFormCode && chartOptions && (
|
||||
<div className="p-1">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Container from '@/components/shared/Container'
|
||||
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { GridDto, ListFormCustomizationTypeEnum } from '@/proxy/form/models'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import Gantt, {
|
||||
|
|
@ -16,7 +16,8 @@ import Gantt, {
|
|||
Validation,
|
||||
} from 'devextreme-react/gantt'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useStylesheet } from '@/utils/hooks/useStylesheet'
|
||||
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
|
||||
import { layoutTypes } from '../admin/listForm/edit/types'
|
||||
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
|
||||
|
|
@ -70,7 +71,10 @@ const GanttView = (props: GanttViewProps) => {
|
|||
const { translate } = useLocalization()
|
||||
const isPwaMode = usePWA()
|
||||
|
||||
const gridRef = useRef<GanttRef>()
|
||||
// Eskiden Helmet'in <link> çocuğuydu; koşul aynen korunuyor.
|
||||
useStylesheet(isSubForm ? null : '/css/gantt/dx-gantt.min.css', 'dx-gantt-css')
|
||||
|
||||
const gridRef = useRef<GanttRef | undefined>(undefined)
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const { checkPermission } = usePermission()
|
||||
|
||||
|
|
@ -80,7 +84,7 @@ const GanttView = (props: GanttViewProps) => {
|
|||
const [scaleType, setScaleType] = useState<GanttScaleType>('weeks')
|
||||
const [taskListWidth, setTaskListWidth] = useState(500)
|
||||
const layout = layoutTypes.gantt || 'gantt'
|
||||
const pendingStateRef = useRef<Record<string, any>>()
|
||||
const pendingStateRef = useRef<Record<string, any> | undefined>(undefined)
|
||||
const loadedStateKeyRef = useRef('')
|
||||
|
||||
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, gridDto?.widgets)
|
||||
|
|
@ -297,15 +301,7 @@ const GanttView = (props: GanttViewProps) => {
|
|||
</div>
|
||||
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
{!isSubForm && (
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + gridDto?.gridOptions.title)}
|
||||
defaultTitle={APP_NAME}
|
||||
>
|
||||
<link rel="stylesheet" href="/css/gantt/dx-gantt.min.css" />
|
||||
</Helmet>
|
||||
)}
|
||||
{!isSubForm && <PageTitle title={translate('::' + gridDto?.gridOptions.title)} />}
|
||||
{!gridDto && (
|
||||
<div className="p-4">
|
||||
<Loading loading>Loading gantt configuration...</Loading>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Container from '@/components/shared/Container'
|
||||
import '@/utils/registerDevExtremeEditors'
|
||||
import { Dialog, Notification, toast } from '@/components/ui'
|
||||
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { GridDto, SubFormTabTypeEnum } from '@/proxy/form/models'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Template } from 'devextreme-react/core/template'
|
||||
|
|
@ -32,7 +32,7 @@ import DataGrid, {
|
|||
import { Item } from 'devextreme-react/toolbar'
|
||||
import CustomStore from 'devextreme/data/custom_store'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import SubForms from '../form/SubForms'
|
||||
import { RowMode } from '../form/types'
|
||||
import { GridColumnData } from './GridColumnData'
|
||||
|
|
@ -112,10 +112,10 @@ const Grid = (props: GridProps) => {
|
|||
const localizationTexts = useStoreState((state) => state.abpConfig.texts)
|
||||
const useMobileEditPopup = shouldUseMobileEditPopup()
|
||||
|
||||
const gridRef = useRef<DataGridRef>()
|
||||
const gridRef = useRef<DataGridRef | undefined>(undefined)
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||
const editingFormInstanceRef = useRef<any>()
|
||||
const editingFormInstanceRef = useRef<any | undefined>(undefined)
|
||||
// Edit popup açıkken grid state'inin kaydedilmesini engeller.
|
||||
const isEditingRef = useRef(false)
|
||||
|
||||
|
|
@ -566,11 +566,7 @@ const Grid = (props: GridProps) => {
|
|||
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
{!isSubForm && (
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + gridOptions?.title)}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::' + gridOptions?.title)} />
|
||||
)}
|
||||
|
||||
{loadingMessage && (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Container from '@/components/shared/Container'
|
||||
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { GridDto, ListFormCustomizationTypeEnum } from '@/proxy/form/models'
|
||||
import { postListFormCustomization } from '@/services/list-form-customization.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
|
|
@ -23,7 +23,7 @@ import PivotGrid, {
|
|||
import CustomStore from 'devextreme/data/custom_store'
|
||||
import type { Field } from 'devextreme/ui/pivot_grid/data_source'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { GridColumnData } from './GridColumnData'
|
||||
import { pivotFieldConvertDataType, setGridPanelColor } from './Utils'
|
||||
import { createConditionalCellStyleHandler } from './shared/cellStyles'
|
||||
|
|
@ -62,7 +62,7 @@ const Pivot = (props: PivotProps) => {
|
|||
const { checkPermission } = usePermission()
|
||||
const isPwaMode = usePWA()
|
||||
|
||||
const gridRef = useRef<PivotGridRef>()
|
||||
const gridRef = useRef<PivotGridRef | undefined>(undefined)
|
||||
const chartRef = useRef<ChartRef>(null)
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const config = useStoreState((state) => state.abpConfig.config)
|
||||
|
|
@ -360,11 +360,7 @@ const Pivot = (props: PivotProps) => {
|
|||
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
{!isSubForm && (
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + gridDto?.gridOptions.title)}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + gridDto?.gridOptions.title)} />
|
||||
)}
|
||||
{gridDto && columnData && (
|
||||
<div className="p-1">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Container from '@/components/shared/Container'
|
||||
import '@/utils/registerDevExtremeEditors'
|
||||
import { APP_NAME, AVATAR_URL, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { AVATAR_URL, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import {
|
||||
ColumnFormatDto,
|
||||
DbTypeEnum,
|
||||
|
|
@ -17,7 +17,7 @@ import Scheduler, {
|
|||
View,
|
||||
} from 'devextreme-react/scheduler'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { getNextSequenceValue } from '@/services/form.service'
|
||||
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
|
||||
import { autoNumber, getEditingFormGroups } from './Utils'
|
||||
|
|
@ -66,7 +66,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
|||
const { translate } = useLocalization()
|
||||
const isPwaMode = usePWA()
|
||||
const useMobileEditPopup = shouldUseMobileEditPopup()
|
||||
const schedulerRef = useRef<SchedulerRef>()
|
||||
const schedulerRef = useRef<SchedulerRef | undefined>(undefined)
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const { checkPermission } = usePermission()
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
|||
|
||||
// Appointment formundaki editorScript'leri Grid/Tree ile aynı altyapıdan çalıştırıyoruz.
|
||||
const appointmentFormDataRef = useRef<Record<string, any>>({})
|
||||
const appointmentFormInstanceRef = useRef<any>()
|
||||
const appointmentFormInstanceRef = useRef<any | undefined>(undefined)
|
||||
const editorScriptHandlers = useMemo(
|
||||
() =>
|
||||
createEditingFormHandlers({
|
||||
|
|
@ -783,11 +783,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
|||
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
{!isSubForm && (
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + gridDto?.gridOptions.title)}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + gridDto?.gridOptions.title)} />
|
||||
)}
|
||||
{!gridDto && (
|
||||
<div className="p-4">
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ const TodoBoardContent = ({
|
|||
?.fieldName ?? 'UserName'
|
||||
const stateStoring = gridDto.gridOptions.stateStoringDto
|
||||
const currentUserName = useStoreState((state) => state.auth.user.userName)
|
||||
const gridRef = useRef<DataGridRef<any, any>>()
|
||||
const gridRef = useRef<DataGridRef<any, any> | undefined>(undefined)
|
||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
||||
const { getBandedColumns, loadLookupDisplayValues } = useListFormColumns({
|
||||
gridDto,
|
||||
|
|
@ -208,7 +208,7 @@ const TodoBoardContent = ({
|
|||
gridDto?: GridDto
|
||||
listFormCode: string
|
||||
columns: GridColumnData[]
|
||||
}>()
|
||||
} | undefined>(undefined)
|
||||
if (
|
||||
!listFormColumnsCacheRef.current ||
|
||||
listFormColumnsCacheRef.current.gridDto !== gridDto ||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Container from '@/components/shared/Container'
|
||||
import '@/utils/registerDevExtremeEditors'
|
||||
import { Dialog, Notification, toast } from '@/components/ui'
|
||||
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { DX_CLASSNAMES } from '@/constants/app.constant'
|
||||
import { GridDto, SubFormTabTypeEnum } from '@/proxy/form/models'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Template } from 'devextreme-react/core/template'
|
||||
|
|
@ -27,7 +27,7 @@ import TreeListDx, {
|
|||
import { Item } from 'devextreme-react/toolbar'
|
||||
import CustomStore from 'devextreme/data/custom_store'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { RowMode } from '../form/types'
|
||||
import { GridColumnData } from './GridColumnData'
|
||||
import GridFilterDialogs from './GridFilterDialogs'
|
||||
|
|
@ -95,10 +95,10 @@ const Tree = (props: TreeProps) => {
|
|||
const localizationTexts = useStoreState((state) => state.abpConfig.texts)
|
||||
const useMobileEditPopup = shouldUseMobileEditPopup()
|
||||
|
||||
const gridRef = useRef<TreeListRef>()
|
||||
const gridRef = useRef<TreeListRef | undefined>(undefined)
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||
const editingFormInstanceRef = useRef<any>()
|
||||
const editingFormInstanceRef = useRef<any | undefined>(undefined)
|
||||
// Edit popup açıkken grid state'inin kaydedilmesini engeller.
|
||||
const isEditingRef = useRef(false)
|
||||
|
||||
|
|
@ -532,11 +532,7 @@ const Tree = (props: TreeProps) => {
|
|||
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
{!isSubForm && (
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + gridOptions?.title)}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::' + gridOptions?.title)} />
|
||||
)}
|
||||
|
||||
{gridDto && gridOptions && columnData && (
|
||||
|
|
|
|||
|
|
@ -49,6 +49,53 @@ const exportCsv = async (component: any, fileName: string) => {
|
|||
saveAs(new Blob([buffer], { type: 'text/csv' }), `${fileName}_export.csv`)
|
||||
}
|
||||
|
||||
/**
|
||||
* jsPDF'in gömülü standart fontları (Helvetica vb.) WinAnsi/Latin-1 kodlamalı;
|
||||
* `ü ö ç` içerirler ama `ı ş ğ İ` içermezler — bunlar Latin-5'e özgüdür. Font
|
||||
* kaydedilmezse o karakterler PDF'te `1 _ 0 boşluk` olarak çıkar. Bu yüzden
|
||||
* Türkçeyi tam kapsayan Roboto gömülüyor (zaten `public/css/fonts` altında,
|
||||
* DevExtreme temaları için duruyor).
|
||||
*
|
||||
* Normal ve bold ayrı ayrı kaydediliyor: PDF exporter başlık satırında bold'a
|
||||
* geçiyor, o stil kayıtlı olmazsa jsPDF varsayılan fonta düşer ve başlıklar
|
||||
* yeniden bozulur.
|
||||
*/
|
||||
const PDF_FONT_FAMILY = 'Roboto'
|
||||
const PDF_FONT_FILES = [
|
||||
{ url: 'css/fonts/Roboto-400.ttf', vfsName: 'Roboto-400.ttf', style: 'normal' },
|
||||
{ url: 'css/fonts/Roboto-700.ttf', vfsName: 'Roboto-700.ttf', style: 'bold' },
|
||||
]
|
||||
|
||||
const toBase64 = (buffer: ArrayBuffer) => {
|
||||
const bytes = new Uint8Array(buffer)
|
||||
// String.fromCharCode'a tek seferde ~170 KB geçmek call stack'i taşırıyor.
|
||||
const CHUNK = 0x8000
|
||||
let binary = ''
|
||||
for (let i = 0; i < bytes.length; i += CHUNK) {
|
||||
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK))
|
||||
}
|
||||
return btoa(binary)
|
||||
}
|
||||
|
||||
/** Font verisi bir kez indirilip sonraki export'lar için saklanır. */
|
||||
let pdfFontsPromise: Promise<{ vfsName: string; style: string; data: string }[]> | undefined
|
||||
|
||||
const loadPdfFonts = () => {
|
||||
pdfFontsPromise ??= Promise.all(
|
||||
PDF_FONT_FILES.map(async ({ url, vfsName, style }) => {
|
||||
const response = await fetch(`${import.meta.env.BASE_URL}${url}`)
|
||||
if (!response.ok) throw new Error(`PDF fontu yüklenemedi: ${url} (${response.status})`)
|
||||
return { vfsName, style, data: toBase64(await response.arrayBuffer()) }
|
||||
}),
|
||||
).catch((error) => {
|
||||
// Tekrar denenebilsin diye başarısız sonucu cache'te bırakma.
|
||||
pdfFontsPromise = undefined
|
||||
throw error
|
||||
})
|
||||
|
||||
return pdfFontsPromise
|
||||
}
|
||||
|
||||
const exportPdf = async (component: any, fileName: string) => {
|
||||
const [jspdfModule, { exportDataGrid }] = await Promise.all([
|
||||
import('jspdf'),
|
||||
|
|
@ -59,6 +106,18 @@ const exportPdf = async (component: any, fileName: string) => {
|
|||
const JsPDFCtor = (jspdfModule as any).default ?? (jspdfModule as any).jsPDF
|
||||
const doc = new JsPDFCtor({})
|
||||
|
||||
try {
|
||||
for (const font of await loadPdfFonts()) {
|
||||
doc.addFileToVFS(font.vfsName, font.data)
|
||||
doc.addFont(font.vfsName, PDF_FONT_FAMILY, font.style)
|
||||
}
|
||||
doc.setFont(PDF_FONT_FAMILY, 'normal')
|
||||
} catch (error) {
|
||||
// Font indirilemezse export'u tamamen düşürmektense standart fontla devam et;
|
||||
// yalnızca Türkçe karakterler bozuk çıkar.
|
||||
console.warn('PDF fontu gömülemedi, standart fontla devam ediliyor:', error)
|
||||
}
|
||||
|
||||
await exportDataGrid({ jsPDFDocument: doc, component, indent: 5 })
|
||||
doc.save(`${fileName}_export.pdf`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export const useListFormGridDto = (listFormCode: string, externalGridDto?: GridD
|
|||
* `calc(100vh - …)` hesabında kullanılır.
|
||||
*/
|
||||
export const useWidgetGroupHeight = (
|
||||
widgetGroupRef: RefObject<HTMLElement>,
|
||||
widgetGroupRef: RefObject<HTMLElement | null>,
|
||||
widgets: unknown,
|
||||
) => {
|
||||
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ export const useListFormStateStoring = ({
|
|||
[filterPrefix, normalizedStorageKey],
|
||||
)
|
||||
|
||||
const saveTimerRef = useRef<ReturnType<typeof setTimeout>>()
|
||||
const pendingStateRef = useRef<any>()
|
||||
const pendingSaveRef = useRef<PendingSave>()
|
||||
const saveTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
|
||||
const pendingStateRef = useRef<any | undefined>(undefined)
|
||||
const pendingSaveRef = useRef<PendingSave | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import {
|
||||
FaBars,
|
||||
FaChevronDown,
|
||||
|
|
@ -15,7 +15,6 @@ import { SortableMenuTree } from './SortableMenuTree'
|
|||
import { collectBranchIds, countMenuItems, filterMenuTree } from './menuTree.utils'
|
||||
import { Container } from '@/components/shared'
|
||||
import { Button, Input, Notification, toast } from '@/components/ui'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { useMenuData } from '@/utils/hooks/useMenuData'
|
||||
|
||||
|
|
@ -115,11 +114,7 @@ export const MenuManager = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Menus.Manager')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.Menus.Manager')} />
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 rounded px-2 lg:px-3 py-3">
|
||||
<div className="flex items-center justify-between mb-2 flex-wrap gap-3">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { AboutDto } from '@/proxy/about/models'
|
||||
import { getAbout, saveAboutPage } from '@/services/about'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { PUBLIC_PAGE_DESIGN } from '@/constants/permission.constant'
|
||||
import {
|
||||
DesignerLayer,
|
||||
|
|
@ -559,11 +558,7 @@ const About: FC = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.About')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.About')} />
|
||||
|
||||
<div className={designerPageClass(isDesignMode, isPanelVisible)}>
|
||||
{/* Hero */}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { FaCalendarAlt, FaUser, FaTag, FaSearch } from 'react-icons/fa'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { showDbDateAsIs } from '@/utils/dateUtils'
|
||||
import { BlogCategory, BlogPost } from '@/proxy/blog/blog'
|
||||
import { blogService } from '@/services/blog.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { Button } from '@/components/ui'
|
||||
import { translateLabel } from './designer'
|
||||
import { PublicPageLoader } from './shared'
|
||||
|
|
@ -105,11 +104,7 @@ const Blog: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.BlogManagement')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.BlogManagement')} />
|
||||
|
||||
{/* Hero */}
|
||||
<div className="relative bg-blue-900 py-12 text-white dark:bg-gray-950">
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { showDbDateAsIs } from '@/utils/dateUtils'
|
||||
import { BlogPost } from '@/proxy/blog/blog'
|
||||
import { blogService } from '@/services/blog.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { useStoreState } from '@/store/store'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { translateLabel } from './designer'
|
||||
import { PublicPageLoader } from './shared'
|
||||
|
||||
|
|
@ -79,11 +78,7 @@ const BlogDetail: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={title || translate('::App.BlogManagement')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={title || translate('::App.BlogManagement')} />
|
||||
<div className="relative bg-blue-900 py-12 text-white dark:bg-gray-950" />
|
||||
|
||||
<div className="container mx-auto px-4">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Basket } from '@/components/orders/Basket'
|
||||
import { TenantForm } from '@/components/orders/TenantForm'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { CustomTenantDto } from '@/proxy/config/models'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { useStoreActions, useStoreState } from '@/store/store'
|
||||
|
|
@ -12,7 +11,7 @@ import {
|
|||
} from '@/utils/basketUtils'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const Checkout: React.FC = () => {
|
||||
|
|
@ -57,11 +56,7 @@ const Checkout: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Public.nav.checkout')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Public.nav.checkout')} />
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="relative bg-blue-900 py-12 text-white dark:bg-gray-950" />
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ import {
|
|||
FaEnvelope,
|
||||
FaIdCard,
|
||||
} from 'react-icons/fa'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { ContactDto } from '@/proxy/contact/models'
|
||||
import { getContact, saveContactPage } from '@/services/contact.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { PUBLIC_PAGE_DESIGN } from '@/constants/permission.constant'
|
||||
import {
|
||||
DesignerLayer,
|
||||
|
|
@ -429,11 +428,7 @@ const Contact: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className={designerPageClass(isDesignMode, isPanelVisible)}>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Contact')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.Contact')} />
|
||||
|
||||
<SelectableBlock
|
||||
id="hero"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { FaArrowRight, FaChevronLeft, FaChevronRight } from 'react-icons/fa'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { PUBLIC_PAGE_DESIGN } from '@/constants/permission.constant'
|
||||
import { Button } from '@/components/ui'
|
||||
import { useOptionalDemo } from '@/contexts/DemoContext'
|
||||
|
|
@ -570,11 +569,7 @@ const Home: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className={designerPageClass(isDesignMode, isPanelVisible)}>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Home')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.Home')} />
|
||||
|
||||
{/* Hero carousel */}
|
||||
<div className="relative">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Basket } from '@/components/orders/Basket'
|
||||
import { PaymentForm } from '@/components/orders/PaymentForm'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { CustomTenantDto } from '@/proxy/config/models'
|
||||
import { OrderDto } from '@/proxy/order/models'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
|
|
@ -14,7 +13,7 @@ import {
|
|||
} from '@/utils/basketUtils'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { PublicPageLoader } from './shared'
|
||||
|
||||
|
|
@ -120,11 +119,7 @@ const Payment: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'Public.nav.payment')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'Public.nav.payment')} />
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="relative bg-blue-900 py-12 text-white dark:bg-gray-950" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { BillingControls } from '@/components/orders/BillingControls'
|
||||
import { Basket } from '@/components/orders/Basket'
|
||||
import { ProductCatalog } from '@/components/orders/ProductCatalog'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { BillingCycle } from '@/proxy/order/models'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { useStoreActions, useStoreState } from '@/store/store'
|
||||
|
|
@ -15,7 +14,7 @@ import {
|
|||
} from '@/utils/basketUtils'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const Products: React.FC = () => {
|
||||
|
|
@ -73,11 +72,7 @@ const Products: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Orders')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::App.Orders')} />
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="relative bg-blue-900 py-14 text-white dark:bg-gray-950"></div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { FaCheckCircle } from 'react-icons/fa'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { ServiceDto } from '@/proxy/services/models'
|
||||
import { getServices, saveServicesPage } from '@/services/service.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { PUBLIC_PAGE_DESIGN } from '@/constants/permission.constant'
|
||||
import {
|
||||
DesignerLayer,
|
||||
|
|
@ -567,11 +566,7 @@ const Services: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className={designerPageClass(isDesignMode, isPanelVisible)}>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Services')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.Services')} />
|
||||
|
||||
{/* Hero */}
|
||||
<SelectableBlock
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { OrderSuccess } from '@/components/orders/OrderSuccess'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { PublicPageLoader } from './shared'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useStoreActions, useStoreState } from '@/store/store'
|
||||
|
||||
|
|
@ -35,11 +34,7 @@ const Success: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.Platform.Success')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'App.Platform.Success')} />
|
||||
<div className="relative bg-blue-900 py-12 text-white dark:bg-gray-950" />
|
||||
|
||||
<div className="container mx-auto px-4 pt-12">
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { lazy, Suspense } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { Container } from '@/components/shared'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { useLocation, useParams } from 'react-router-dom'
|
||||
import { RequestOptions } from 'devexpress-reporting-react/dx-report-designer'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { useReportRequestSetup, useReportShellTheme, useReportUrl } from './shared/reportShell'
|
||||
|
||||
const ReportDesigner = lazy(() => import('devexpress-reporting-react/dx-report-designer'))
|
||||
|
|
@ -31,11 +30,7 @@ const DevexpressReportDesigner: FC = () => {
|
|||
|
||||
return (
|
||||
<Container className={shellClassName}>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Reports')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.Reports')} />
|
||||
|
||||
{isRequestConfigured && (
|
||||
<Suspense fallback={<div>{translate('::App.Loading')}</div>}>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useRef } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { Container } from '@/components/shared'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { useParams, useLocation } from 'react-router-dom'
|
||||
import ReportViewer, {
|
||||
|
|
@ -9,7 +9,6 @@ import ReportViewer, {
|
|||
RequestOptions,
|
||||
} from 'devexpress-reporting-react/dx-report-viewer'
|
||||
import { ZoomAutoBy } from 'devexpress-reporting/viewer/constants'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { useReportRequestSetup, useReportShellTheme, useReportUrl } from './shared/reportShell'
|
||||
|
||||
const DEFAULT_REPORT_ZOOM = 1
|
||||
|
|
@ -38,7 +37,7 @@ const DevexpressReportViewer: FC = () => {
|
|||
id: string
|
||||
}>()
|
||||
const location = useLocation()
|
||||
const previewModelRef = useRef<ReportPreviewModel>()
|
||||
const previewModelRef = useRef<ReportPreviewModel | undefined>(undefined)
|
||||
const { shellClassName } = useReportShellTheme()
|
||||
const { cultureName, isRequestConfigured } = useReportRequestSetup()
|
||||
const reportUrlWithParams = useReportUrl(
|
||||
|
|
@ -92,11 +91,7 @@ const DevexpressReportViewer: FC = () => {
|
|||
|
||||
return (
|
||||
<Container className={shellClassName}>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.Reports')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.Reports')} />
|
||||
|
||||
{isRequestConfigured && (
|
||||
<ReportViewer key={reportUrlWithParams} reportUrl={reportUrlWithParams}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { ajaxSetup } from '@devexpress/analytics-core/analytics-utils-native'
|
||||
import { fetchSetup } from '@devexpress/analytics-core/analytics-utils-native'
|
||||
import { MODE_DARK } from '@/constants/theme.constant'
|
||||
import { useStoreState } from '@/store'
|
||||
import {
|
||||
|
|
@ -95,9 +95,18 @@ export const useReportCultureName = () => {
|
|||
}
|
||||
|
||||
/**
|
||||
* DevExpress istekleri global ajax ayarları üzerinden gittiği için oturum ve dil
|
||||
* DevExpress istekleri global istek yöneticisi üzerinden gittiği için oturum ve dil
|
||||
* başlıkları bileşen render edilmeden önce yazılır. `isRequestConfigured` false iken
|
||||
* rapor bileşeni render edilmemelidir, aksi hâlde ilk istek yetkisiz gider.
|
||||
*
|
||||
* Neden `ajaxSetup` değil de `fetchSetup`:
|
||||
* Export'un son adımı (`getExportResult`) `_doExportSync` içinde ikiye ayrılıyor —
|
||||
* `_isFetchConfigured()` true ise fetch ile gidiyor ve buradaki başlıkları taşıyor,
|
||||
* false ise gizli bir `<form>` POST'u ile gidiyor. Form POST'u `Authorization`
|
||||
* başlığı taşıyamaz, `[Authorize]` olan DXXRDV controller'ı 401 döner ve istek bir
|
||||
* iframe içinde olduğu için konsola hiçbir hata düşmez: export sessizce ölür.
|
||||
* `fetchSetup` kullanmak `useFetch`'i açar ve indirmeyi de yetkili yola sokar.
|
||||
* İkisi aynı anda yapılandırılamaz; DevExpress bu durumda hata fırlatır.
|
||||
*/
|
||||
export const useReportRequestSetup = () => {
|
||||
const token = useStoreState((state) => state.auth.session.token)
|
||||
|
|
@ -106,11 +115,11 @@ export const useReportRequestSetup = () => {
|
|||
const [configuredRequestKey, setConfiguredRequestKey] = useState<string>()
|
||||
|
||||
useEffect(() => {
|
||||
const nextHeaders = { ...(ajaxSetup.ajaxSettings.headers ?? {}) }
|
||||
const nextHeaders = { ...(fetchSetup.fetchSettings.headers ?? {}) }
|
||||
delete nextHeaders.Authorization
|
||||
delete nextHeaders['Accept-Language']
|
||||
|
||||
ajaxSetup.ajaxSettings.headers = {
|
||||
fetchSetup.fetchSettings.headers = {
|
||||
...nextHeaders,
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...(cultureName ? { 'Accept-Language': cultureName } : {}),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
toast,
|
||||
} from '@/components/ui'
|
||||
import Notification from '@/components/ui/Notification'
|
||||
import { APP_NAME, MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||||
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||||
import { MainGroupedSettingDto } from '@/proxy/settings/models'
|
||||
import { getList, updateSettingValues } from '@/services/setting-ui.service'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
|
|
@ -22,7 +22,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { FaQuestionCircle } from 'react-icons/fa'
|
||||
|
||||
function getOptions(selectOptions?: Record<string, string>) {
|
||||
|
|
@ -116,11 +116,7 @@ const Settings = () => {
|
|||
|
||||
return (
|
||||
<Container className="h-full">
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::' + 'App.Settings')}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<PageTitle title={translate('::' + 'App.Settings')} />
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="md:w-2/12 min-w-fit">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import AdaptableCard from '@/components/shared/AdaptableCard'
|
||||
import Container from '@/components/shared/Container'
|
||||
import { Button, Notification, toast } from '@/components/ui'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import PageTitle from '@/components/shared/PageTitle'
|
||||
import { FaRegCheckCircle, FaSyncAlt, FaTag } from 'react-icons/fa'
|
||||
import { releaseNoteService, type ReleaseNoteDto } from '@/services/releaseNote.service'
|
||||
import { checkForAppUpdate, clearPendingChangelog } from './swRegistration'
|
||||
|
|
@ -125,11 +124,7 @@ const Changelog = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<Helmet
|
||||
titleTemplate={`%s | ${APP_NAME}`}
|
||||
title={translate('::App.ChangeLog')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<PageTitle title={translate('::App.ChangeLog')} />
|
||||
<AdaptableCard>
|
||||
<div className="p-2">
|
||||
<div className="mb-6 flex flex-col gap-3 border-b border-gray-200 pb-5 dark:border-gray-700 sm:flex-row sm:items-center sm:justify-between">
|
||||
|
|
|
|||
Loading…
Reference in a new issue