Yetki düzenlemesi, Access Denied ve NotFound

This commit is contained in:
Sedat Öztürk 2025-08-12 22:42:32 +03:00
parent 821806a8db
commit e97d560761
13 changed files with 180 additions and 87 deletions

View file

@ -2497,6 +2497,12 @@
"en": "Access Failed Count", "en": "Access Failed Count",
"tr": "Başarısız Erişim Sayısı" "tr": "Başarısız Erişim Sayısı"
}, },
{
"resourceName": "Platform",
"key": "Abp.Identity.Ai",
"en": "AI Assistant",
"tr": "Yapay Zeka Asistanı"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "Abp.Identity.Profile", "key": "Abp.Identity.Profile",
@ -6352,7 +6358,7 @@
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "Public.notFound.button", "key": "Public.notFound.button",
"tr": "Ana Sayfaya Dön", "tr": "Ana Sayfa'ya Dön",
"en": "Return to Homepage" "en": "Return to Homepage"
}, },
{ {
@ -10602,6 +10608,14 @@
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3 "MultiTenancySide": 3
}, },
{
"GroupName": "AbpIdentity",
"Name": "Abp.Identity.Ai",
"ParentName": null,
"DisplayName": "Abp.Identity.Ai",
"IsEnabled": true,
"MultiTenancySide": 3
},
{ {
"GroupName": "AbpIdentity", "GroupName": "AbpIdentity",
"Name": "Abp.Identity.PermissionGroups", "Name": "Abp.Identity.PermissionGroups",
@ -25519,14 +25533,14 @@
"path": "/admin/menuManager", "path": "/admin/menuManager",
"componentPath": "@/views/menu/MenuManager", "componentPath": "@/views/menu/MenuManager",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.Menus.Manager"]
}, },
{ {
"key": "admin.listFormManagement.wizard", "key": "admin.listFormManagement.wizard",
"path": "/admin/listform/wizard", "path": "/admin/listform/wizard",
"componentPath": "@/views/admin/listForm/Wizard", "componentPath": "@/views/admin/listForm/Wizard",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.Listforms.Wizard"]
}, },
{ {
"key": "admin.listFormManagement.edit", "key": "admin.listFormManagement.edit",
@ -25547,14 +25561,14 @@
"path": "/admin/forumManagement", "path": "/admin/forumManagement",
"componentPath": "@/views/forum/Management", "componentPath": "@/views/forum/Management",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.ForumManagement"]
}, },
{ {
"key": "admin.ai", "key": "admin.ai",
"path": "/admin/ai", "path": "/admin/ai",
"componentPath": "@/views/ai/Assistant", "componentPath": "@/views/ai/Assistant",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["Abp.Identity.Ai"]
}, },
{ {
"key": "admin.profile.general", "key": "admin.profile.general",
@ -25596,28 +25610,28 @@
"path": "/admin/settings", "path": "/admin/settings",
"componentPath": "@/views/settings/Settings", "componentPath": "@/views/settings/Settings",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.Setting"]
}, },
{ {
"key": "admin.identity.user.detail", "key": "admin.identity.user.detail",
"path": "/admin/users/detail/:userId", "path": "/admin/users/detail/:userId",
"componentPath": "@/views/admin/user-management/Details", "componentPath": "@/views/admin/user-management/Details",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["AbpIdentity.Users.Update"]
}, },
{ {
"key": "admin.identity.ous", "key": "admin.identity.ous",
"path": "/admin/ous", "path": "/admin/ous",
"componentPath": "@/views/admin/organization-unit/OrganizationUnits", "componentPath": "@/views/admin/organization-unit/OrganizationUnits",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["Abp.Identity.OrganizationUnits"]
}, },
{ {
"key": "admin.forum", "key": "admin.forum",
"path": "/admin/forum", "path": "/admin/forum",
"componentPath": "@/views/forum/Forum", "componentPath": "@/views/forum/Forum",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.ForumManagement.Publish"]
}, },
{ {
"key": "admin.list", "key": "admin.list",
@ -25666,70 +25680,70 @@
"path": "/admin/developerkit", "path": "/admin/developerkit",
"componentPath": "@/views/developerKit/DashboardPage", "componentPath": "@/views/developerKit/DashboardPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit"]
}, },
{ {
"key": "admin.developerkit.entities", "key": "admin.developerkit.entities",
"path": "/admin/developerkit/entities", "path": "/admin/developerkit/entities",
"componentPath": "@/views/developerKit/EntityPage", "componentPath": "@/views/developerKit/EntityPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Entity"]
}, },
{ {
"key": "admin.developerkit.entities.new", "key": "admin.developerkit.entities.new",
"path": "/admin/developerkit/entities/new", "path": "/admin/developerkit/entities/new",
"componentPath": "@/views/developerKit/EntityDetailPage", "componentPath": "@/views/developerKit/EntityDetailPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Entity"]
}, },
{ {
"key": "admin.developerkit.entities.edit", "key": "admin.developerkit.entities.edit",
"path": "/admin/developerkit/entities/edit/:id", "path": "/admin/developerkit/entities/edit/:id",
"componentPath": "@/views/developerKit/EntityDetailPage", "componentPath": "@/views/developerKit/EntityDetailPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Entity"]
}, },
{ {
"key": "admin.developerkit.migrations", "key": "admin.developerkit.migrations",
"path": "/admin/developerkit/migrations", "path": "/admin/developerkit/migrations",
"componentPath": "@/views/developerKit/MigrationPage", "componentPath": "@/views/developerKit/MigrationPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Migrations"]
}, },
{ {
"key": "admin.developerkit.endpoints", "key": "admin.developerkit.endpoints",
"path": "/admin/developerkit/endpoints", "path": "/admin/developerkit/endpoints",
"componentPath": "@/views/developerKit/EndpointPage", "componentPath": "@/views/developerKit/EndpointPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Endpoints"]
}, },
{ {
"key": "admin.developerkit.components", "key": "admin.developerkit.components",
"path": "/admin/developerkit/components", "path": "/admin/developerkit/components",
"componentPath": "@/views/developerKit/ComponentPage", "componentPath": "@/views/developerKit/ComponentPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Components"]
}, },
{ {
"key": "admin.developerkit.components.new", "key": "admin.developerkit.components.new",
"path": "/admin/developerkit/components/new", "path": "/admin/developerkit/components/new",
"componentPath": "@/views/developerKit/ComponentDetailPage", "componentPath": "@/views/developerKit/ComponentDetailPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Components"]
}, },
{ {
"key": "admin.developerkit.components.view", "key": "admin.developerkit.components.view",
"path": "/admin/developerkit/components/view/:id", "path": "/admin/developerkit/components/view/:id",
"componentPath": "@/views/developerKit/ComponentDetailPage", "componentPath": "@/views/developerKit/ComponentDetailPage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Components"]
}, },
{ {
"key": "admin.developerkit.components.edit", "key": "admin.developerkit.components.edit",
"path": "/admin/developerkit/components/edit/:id", "path": "/admin/developerkit/components/edit/:id",
"componentPath": "@/views/developerKit/CodePage", "componentPath": "@/views/developerKit/CodePage",
"routeType": "protected", "routeType": "protected",
"authority": [] "authority": ["App.DeveloperKit.Components" ]
} }
], ],
"CustomEndpoints": [ "CustomEndpoints": [

View file

@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "index.html", "url": "index.html",
"revision": "0.0ii4qr2m5co" "revision": "0.rs1dfl4jqho"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

View file

@ -38,58 +38,58 @@ const PublicLayout = lazy(() => import('./PublicLayout'))
const Layout = () => { const Layout = () => {
const location = useLocation() const location = useLocation()
const layoutType = useStoreState((state) => state.theme.layout.type) as LayoutType const layoutType = useStoreState((s) => s.theme.layout.type) as LayoutType
const { routes, loading } = useDynamicRoutes() const { routes, loading } = useDynamicRoutes()
const { authenticated } = useAuth() const { authenticated } = useAuth()
useDirection() // Her zaman çağrılmalı useDirection()
useLocale() // Her zaman çağrılmalı useLocale()
const currentPath = location.pathname const currentPath = location.pathname
const isAdminPath = currentPath.startsWith('/admin')
const route = useMemo(() => { const route = useMemo(() => {
if (!routes || routes.length === 0) return undefined if (!routes || routes.length === 0) return undefined
const matched = routes.find((route) => { const matched = routes.find((route) => {
if (!route.path.includes(':')) return route.path === currentPath if (!route.path.includes(':')) return route.path === currentPath
const regexPath = route.path.replace(/:[^/]+/g, '[^/]+') const regexPath = route.path.replace(/:[^/]+/g, '[^/]+')
const regex = new RegExp(`^${regexPath}$`) const regex = new RegExp(`^${regexPath}$`)
return regex.test(currentPath) return regex.test(currentPath)
}) })
return matched return matched
}, [routes, currentPath]) }, [routes, currentPath])
const AppLayout = useMemo(() => { const AppLayout = useMemo(() => {
if (authenticated && route?.routeType === 'protected') { // 1) Admin path ise, route bulunmasa bile admin layout'u göster
if (isAdminPath) {
return layouts[layoutType] return layouts[layoutType]
} }
if (route?.routeType === 'authenticated' || hasSubdomain()) { // 2) Admin değil ve route bulunamadı -> PublicLayout
return AuthLayout if (!route) {
return PublicLayout
} }
// 3) Mevcut kurallar
if (authenticated && route.routeType === 'protected') {
return layouts[layoutType]
}
if (route.routeType === 'authenticated' || hasSubdomain()) {
return AuthLayout
}
return PublicLayout return PublicLayout
}, [layoutType, authenticated, route]) }, [isAdminPath, route, layoutType, authenticated])
// ❗ KOŞUL BURADA OLMALI: HOOKLARDAN SONRA
if (loading) { if (loading) {
return ( return (
<div className="flex flex-auto flex-col h-[100vh]"> <div className="flex flex-auto flex-col h-[100vh]">
<Loading loading={true} /> <Loading loading />
</div> </div>
) )
} }
return ( return (
<Suspense <Suspense fallback={<div className="flex flex-auto flex-col h-[100vh]"><Loading loading /></div>}>
fallback={
<div className="flex flex-auto flex-col h-[100vh]">
<Loading loading={true} />
</div>
}
>
<AppLayout /> <AppLayout />
</Suspense> </Suspense>
) )

View file

@ -1,18 +1,25 @@
// AuthorityGuard.tsx
import { PropsWithChildren } from 'react' import { PropsWithChildren } from 'react'
import { Navigate } from 'react-router-dom' import { Navigate, useLocation } from 'react-router-dom'
import useAuthority from '@/utils/hooks/useAuthority' import useAuthority from '@/utils/hooks/useAuthority'
import { getAccessDeniedPath } from '@/utils/routing'
type AuthorityGuardProps = PropsWithChildren<{ type AuthorityGuardProps = PropsWithChildren<{
userAuthority?: string[] userAuthority?: string[]
authority?: string[] authority?: string[]
}> }>
const AuthorityGuard = (props: AuthorityGuardProps) => { const AuthorityGuard = (props: AuthorityGuardProps) => {
const { userAuthority = [], authority = [], children } = props const { userAuthority = [], authority = [], children } = props
const roleMatched = useAuthority(userAuthority, authority)
const location = useLocation()
const roleMatched = useAuthority(userAuthority, authority) if (!roleMatched) {
const to = getAccessDeniedPath(location.pathname)
return <Navigate to={to} replace state={{ from: location }} />
}
return <>{roleMatched ? children : <Navigate to="/access-denied" />}</> return <>{children}</>
} }
export default AuthorityGuard export default AuthorityGuard

View file

@ -1,18 +1,24 @@
// PermissionGuard.tsx
import { usePermission } from '@/utils/hooks/usePermission' import { usePermission } from '@/utils/hooks/usePermission'
import { PropsWithChildren } from 'react' import { PropsWithChildren } from 'react'
import { Navigate } from 'react-router-dom' import { Navigate, useLocation } from 'react-router-dom'
import { getAccessDeniedPath } from '@/utils/routing'
type PermissionGuardProps = PropsWithChildren<{ type PermissionGuardProps = PropsWithChildren<{
permissions?: string[] permissions?: string[]
}> }>
const PermissionGuard = (props: PermissionGuardProps) => { const PermissionGuard = ({ permissions = [], children }: PermissionGuardProps) => {
const { permissions = [], children } = props
const { checkPermissions } = usePermission() const { checkPermissions } = usePermission()
const permissionsMatched = checkPermissions(permissions) const permissionsMatched = checkPermissions(permissions)
const location = useLocation()
return <>{permissionsMatched ? children : <Navigate to="/access-denied" />}</> if (!permissionsMatched) {
const to = getAccessDeniedPath(location.pathname)
return <Navigate to={to} replace state={{ from: location }} />
}
return <>{children}</>
} }
export default PermissionGuard export default PermissionGuard

View file

@ -1,6 +1,7 @@
import Tooltip from '@/components/ui/Tooltip' import Tooltip from '@/components/ui/Tooltip'
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { usePermission } from '@/utils/hooks/usePermission'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { FcAssistant } from 'react-icons/fc' import { FcAssistant } from 'react-icons/fc'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
@ -8,15 +9,17 @@ import { useNavigate } from 'react-router-dom'
const AiAssistant = () => { const AiAssistant = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const navigate = useNavigate() const navigate = useNavigate()
const { checkPermissions } = usePermission()
const canViewAi = checkPermissions(['Abp.Identity.Ai'])
if (!canViewAi) {
return null
}
return ( return (
<> <>
<Helmet <Tooltip title={translate('::Abp.Identity.Ai')}>
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'AI Assistant')}
defaultTitle="Kurs Platform"
></Helmet>
<Tooltip title="AI Asistan">
<div <div
onClick={() => navigate(ROUTES_ENUM.protected.admin.ai)} onClick={() => navigate(ROUTES_ENUM.protected.admin.ai)}
className="flex items-center justify-center w-9 h-9 m-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors duration-200" className="flex items-center justify-center w-9 h-9 m-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors duration-200"

View file

@ -1,4 +1,5 @@
import React, { useMemo } from 'react' // DynamicRouter.tsx
import React from 'react'
import { Routes, Route, Navigate } from 'react-router-dom' import { Routes, Route, Navigate } from 'react-router-dom'
import { mapDynamicRoutes, loadComponent } from './dynamicRouteLoader' import { mapDynamicRoutes, loadComponent } from './dynamicRouteLoader'
import { useDynamicRoutes } from './dynamicRoutesContext' import { useDynamicRoutes } from './dynamicRoutesContext'
@ -8,17 +9,20 @@ import PageContainer from '@/components/template/PageContainer'
import { ROUTES_ENUM } from './route.constant' import { ROUTES_ENUM } from './route.constant'
import { hasSubdomain } from '@/utils/subdomain' import { hasSubdomain } from '@/utils/subdomain'
// AccessDenied ve NotFound'u dinamiklikten çıkarıyoruz
const AccessDenied = React.lazy(() => import('@/views/AccessDenied'))
const NotFound = React.lazy(() => import('@/views/NotFound'))
export const DynamicRouter: React.FC = () => { export const DynamicRouter: React.FC = () => {
const { routes, loading, error } = useDynamicRoutes() const { routes, loading, error } = useDynamicRoutes()
const dynamicRoutes = React.useMemo(() => mapDynamicRoutes(routes), [routes])
const dynamicRoutes = useMemo(() => mapDynamicRoutes(routes), [routes])
const NotFoundComponent = useMemo(() => loadComponent('views/NotFound'), [])
if (loading) return <div>Loading...</div> if (loading) return <div>Loading...</div>
if (error) return <div>Hata: {error}</div> if (error) return <div>Hata: {error}</div>
return ( return (
<Routes> <Routes>
{/* ADMIN */}
<Route path="/admin/*" element={<ProtectedRoute />}> <Route path="/admin/*" element={<ProtectedRoute />}>
{dynamicRoutes {dynamicRoutes
.filter((r) => r.routeType === 'protected') .filter((r) => r.routeType === 'protected')
@ -27,7 +31,7 @@ export const DynamicRouter: React.FC = () => {
return ( return (
<Route <Route
key={route.key} key={route.key}
path={route.path.replace(/^\/admin\/?/, '') || '.'} // Remove /admin prefix for nested routes path={route.path.replace(/^\/admin\/?/, '') || '.'}
element={ element={
<PermissionGuard permissions={route.authority}> <PermissionGuard permissions={route.authority}>
<PageContainer> <PageContainer>
@ -40,9 +44,36 @@ export const DynamicRouter: React.FC = () => {
/> />
) )
})} })}
{/* admin default */}
<Route index element={<Navigate to={ROUTES_ENUM.protected.dashboard} replace />} /> <Route index element={<Navigate to={ROUTES_ENUM.protected.dashboard} replace />} />
{/* admin access denied (statik) */}
<Route
path="access-denied"
element={
<PageContainer>
<React.Suspense fallback={<div>Loading...</div>}>
<AccessDenied />
</React.Suspense>
</PageContainer>
}
/>
{/* admin not found (statik) */}
<Route
path="*"
element={
<PageContainer>
<React.Suspense fallback={<div>Loading...</div>}>
<NotFound />
</React.Suspense>
</PageContainer>
}
/>
</Route> </Route>
{/* Auth/Public dinamik rotalar */}
{dynamicRoutes {dynamicRoutes
.filter((r) => .filter((r) =>
hasSubdomain() ? r.routeType === 'authenticated' : r.routeType !== 'protected', hasSubdomain() ? r.routeType === 'authenticated' : r.routeType !== 'protected',
@ -62,6 +93,7 @@ export const DynamicRouter: React.FC = () => {
) )
})} })}
{/* root redirect */}
<Route <Route
path="/" path="/"
element={ element={
@ -72,16 +104,23 @@ export const DynamicRouter: React.FC = () => {
} }
/> />
{/* public access denied (statik) */}
<Route
path="/access-denied"
element={
<React.Suspense fallback={<div>Loading...</div>}>
<AccessDenied />
</React.Suspense>
}
/>
{/* public not found (statik) */}
<Route <Route
path="*" path="*"
element={ element={
hasSubdomain() ? ( <React.Suspense fallback={<div>Loading...</div>}>
<Navigate to={ROUTES_ENUM.authenticated.login} replace /> <NotFound />
) : ( </React.Suspense>
<React.Suspense fallback={<div>Loading...</div>}>
<NotFoundComponent />
</React.Suspense>
)
} }
/> />
</Routes> </Routes>

View file

@ -7,6 +7,7 @@ export const ROUTES_ENUM = {
demo: '/demo', demo: '/demo',
blog: '/blog', blog: '/blog',
contact: '/contact', contact: '/contact',
accessDenied: '/access-denied',
}, },
authenticated: { authenticated: {
login: '/login', login: '/login',
@ -67,5 +68,6 @@ export const ROUTES_ENUM = {
chart: '/admin/chart/:chartCode', chart: '/admin/chart/:chartCode',
pivot: '/admin/pivot/:listFormCode', pivot: '/admin/pivot/:listFormCode',
}, },
accessDenied: '/admin/access-denied',
}, },
} }

5
ui/src/utils/routing.ts Normal file
View file

@ -0,0 +1,5 @@
import { ROUTES_ENUM } from "@/routes/route.constant";
// src/utils/routing.ts
export const getAccessDeniedPath = (pathname: string) =>
pathname.startsWith('/admin') ? ROUTES_ENUM.protected.accessDenied : ROUTES_ENUM.public.accessDenied

View file

@ -10,19 +10,27 @@ const AccessDenied = () => {
const navigate = useNavigate() const navigate = useNavigate()
const { translate } = useLocalization() const { translate } = useLocalization()
const currentPath = location.pathname
const isAdminPath = currentPath.startsWith('/admin')
return ( return (
<Container className="h-full"> <Container className="h-full">
<div className="h-full flex flex-col items-center justify-center p-24"> <div className="h-full flex flex-col items-center justify-center p-28">
<DoubleSidedImage <DoubleSidedImage
src="/img/others/img-2.png" src="/img/others/img-2.png"
darkModeSrc="/img/others/img-2-dark.png" darkModeSrc="/img/others/img-2-dark.png"
alt={ translate('::AccessDenied')} alt={translate('::AccessDenied')}
/> />
<div className="mt-6 text-center"> <div className="mt-6 text-center">
<h3 className="mb-2">{ translate('::AccessDenied')}</h3> <h3 className="mb-2">{translate('::AccessDenied')}</h3>
<p className="text-base">{ translate('::AccessDeniedMessage')}</p> <p className="text-base">{translate('::AccessDeniedMessage')}</p>
</div> </div>
<Button size="xs" className="mt-2" variant="default" onClick={() => navigate(ROUTES_ENUM.protected.dashboard)}> <Button
size="xs"
className="mt-2"
variant="default"
onClick={() => navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)}
>
<MdArrowBack /> <MdArrowBack />
</Button> </Button>
</div> </div>

View file

@ -1,25 +1,30 @@
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
const NotFoundPage = () => { const NotFoundPage = () => {
const navigate = useNavigate() const navigate = useNavigate()
const { translate } = useLocalization()
const currentPath = location.pathname
const isAdminPath = currentPath.startsWith('/admin')
return ( return (
<div className="p-20"> <div className="p-28">
<div className="flex items-center justify-center font-inter"> <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"> <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 404
</div> </div>
</div> </div>
<p className="flex items-center justify-center text-xl mb-6 text-gray-600"> <p className="flex items-center justify-center text-xl mb-6 text-gray-600">
Aradığınız sayfa bulunamadı. {translate('::Public.notFound.message')}
</p> </p>
<div className="flex items-center justify-center font-inter"> <div className="flex items-center justify-center font-inter">
<button <button
onClick={() => navigate(ROUTES_ENUM.protected.dashboard)} onClick={() => navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)}
className="px-6 py-3 bg-primary rounded-xl shadow hover:bg-secondary transition" className="px-6 py-3 bg-primary rounded-xl shadow hover:bg-secondary transition"
> >
Ana Sayfa'ya Dön {translate('::Public.notFound.button')}
</button> </button>
</div> </div>
</div> </div>

View file

@ -10,6 +10,7 @@ import DialogProvider from './shared/DialogContext'
import DialogShowComponent from './shared/DialogContext/DialogShowComponent' import DialogShowComponent from './shared/DialogContext/DialogShowComponent'
import UiDialog from './shared/UiDialog' import UiDialog from './shared/UiDialog'
import { DynamicRouter } from '@/routes/dynamicRouter' import { DynamicRouter } from '@/routes/dynamicRouter'
import { getAccessDeniedPath } from '@/utils/routing'
interface ViewsProps { interface ViewsProps {
pageContainerType?: 'default' | 'gutterless' | 'contained' pageContainerType?: 'default' | 'gutterless' | 'contained'
@ -48,7 +49,11 @@ const Views = (props: ViewsProps) => {
</Alert> </Alert>
)} )}
{errors?.some((e) => e.statusCode === '403' || e.statusCode === '401') && ( {errors?.some((e) => e.statusCode === '403' || e.statusCode === '401') && (
<Navigate to="/access-denied" replace /> <Navigate
to={getAccessDeniedPath(location.pathname)}
replace
state={{ from: location }}
/>
)} )}
{errors?.map((e) => ( {errors?.map((e) => (
<UiDialog <UiDialog

View file

@ -14,6 +14,7 @@ import { GridColumnData } from '../list/GridColumnData'
import { addCss, addJs } from '../list/Utils' import { addCss, addJs } from '../list/Utils'
import { PermissionResults, RowMode, SimpleItemWithColData } from './types' import { PermissionResults, RowMode, SimpleItemWithColData } from './types'
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models' import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models'
import { getAccessDeniedPath } from '@/utils/routing'
const useGridData = (props: { const useGridData = (props: {
mode: RowMode mode: RowMode
@ -287,16 +288,14 @@ const useGridData = (props: {
// Auth check // Auth check
useEffect(() => { useEffect(() => {
if (!permissionResults) { if (!permissionResults) return
return
}
if ( const noCreate = mode === 'new' && !permissionResults.c
(mode === 'new' && !permissionResults.c) || const noUpdate = mode === 'edit' && !permissionResults.u
(mode === 'edit' && !permissionResults.u) || const noRead = mode === 'view' && !permissionResults.r
(mode === 'view' && !permissionResults.r)
) { if (noCreate || noUpdate || noRead) {
navigate('/access-denied') navigate(getAccessDeniedPath(location.pathname), { replace: true, state: { from: location } })
} }
}, [permissionResults]) }, [permissionResults])