Routes güncellemeleri

This commit is contained in:
Sedat ÖZTÜRK 2025-08-21 17:57:00 +03:00
parent bbc2c9c1cb
commit 614be0c916
15 changed files with 423 additions and 295 deletions

View file

@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react' import React, { useState, useEffect, useCallback } from 'react'
import { useParams, useNavigate } from 'react-router-dom' import { useParams, useNavigate } from 'react-router-dom'
import { useComponents } from '../../contexts/ComponentContext' import { useComponents } from '../../contexts/ComponentContext'
import { FaRegSave, FaArrowLeft, FaExclamationCircle, FaSync } from 'react-icons/fa'; import { FaRegSave, FaArrowLeft, FaExclamationCircle, FaSync } from 'react-icons/fa'
import { parseReactCode } from '../../utils/codeParser' import { parseReactCode } from '../../utils/codeParser'
import ComponentPreview from '../../components/componentEditor/ComponentPreview' import ComponentPreview from '../../components/componentEditor/ComponentPreview'
import { EditorState } from '../../@types/componentInfo' import { EditorState } from '../../@types/componentInfo'
@ -129,7 +129,7 @@ const ComponentEditor: React.FC = () => {
addComponent(componentData) addComponent(componentData)
} }
navigate(ROUTES_ENUM.protected.saas.developerKitComponents) navigate(ROUTES_ENUM.protected.saas.developerKit.components)
} catch (error) { } catch (error) {
console.error('Error saving component:', error) console.error('Error saving component:', error)
alert('Failed to save component. Please try again.') alert('Failed to save component. Please try again.')
@ -159,7 +159,7 @@ const ComponentEditor: React.FC = () => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<button <button
onClick={() => navigate(ROUTES_ENUM.protected.saas.developerKitComponents)} onClick={() => navigate(ROUTES_ENUM.protected.saas.developerKit.components)}
className="flex items-center gap-2 text-slate-600 hover:text-slate-900 transition-colors" className="flex items-center gap-2 text-slate-600 hover:text-slate-900 transition-colors"
> >
<FaArrowLeft className="w-4 h-4" /> <FaArrowLeft className="w-4 h-4" />

View file

@ -96,7 +96,7 @@ const ComponentManager: React.FC = () => {
<p className="text-slate-600">{translate('::App.DeveloperKit.Component.Description')}</p> <p className="text-slate-600">{translate('::App.DeveloperKit.Component.Description')}</p>
</div> </div>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitComponentsNew} to={ROUTES_ENUM.protected.saas.developerKit.componentsNew}
className="flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors" className="flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
> >
<FaPlus className="w-4 h-4" /> <FaPlus className="w-4 h-4" />
@ -245,7 +245,7 @@ const ComponentManager: React.FC = () => {
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitComponentsEdit.replace( to={ROUTES_ENUM.protected.saas.developerKit.componentsEdit.replace(
':id', ':id',
component.id, component.id,
)} )}
@ -256,7 +256,7 @@ const ComponentManager: React.FC = () => {
<FaRegEdit className="w-4 h-4" /> <FaRegEdit className="w-4 h-4" />
</Link> </Link>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitComponentsView.replace( to={ROUTES_ENUM.protected.saas.developerKit.componentsView.replace(
':id', ':id',
component.id, component.id,
)} )}
@ -296,7 +296,7 @@ const ComponentManager: React.FC = () => {
</p> </p>
{!searchTerm && filterActive === 'all' && ( {!searchTerm && filterActive === 'all' && (
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitComponentsNew} to={ROUTES_ENUM.protected.saas.developerKit.componentsNew}
className="inline-flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors" className="inline-flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
> >
<FaPlus className="w-4 h-4" /> <FaPlus className="w-4 h-4" />

View file

@ -1,8 +1,8 @@
import React from "react"; import React from 'react'
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom'
import { useComponents } from "../../contexts/ComponentContext"; import { useComponents } from '../../contexts/ComponentContext'
import { useEntities } from "../../contexts/EntityContext"; import { useEntities } from '../../contexts/EntityContext'
import { useSystemHealth } from "../../utils/hooks/useDeveloperKit"; import { useSystemHealth } from '../../utils/hooks/useDeveloperKit'
import { import {
FaDatabase, FaDatabase,
FaBolt, FaBolt,
@ -15,15 +15,15 @@ import {
FaArrowRight, FaArrowRight,
FaExclamationCircle, FaExclamationCircle,
FaWifi, FaWifi,
FaWindowClose FaWindowClose,
} from 'react-icons/fa'; } from 'react-icons/fa'
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'
const Dashboard: React.FC = () => { const Dashboard: React.FC = () => {
const { components } = useComponents(); const { components } = useComponents()
const { entities, migrations, generatedEndpoints } = useEntities(); const { entities, migrations, generatedEndpoints } = useEntities()
const { isOnline, lastCheck, recheckHealth } = useSystemHealth(); const { isOnline, lastCheck, recheckHealth } = useSystemHealth()
const { translate } = useLocalization() const { translate } = useLocalization()
const stats = [ const stats = [
@ -32,38 +32,38 @@ const Dashboard: React.FC = () => {
value: entities.filter((e) => e.isActive).length, value: entities.filter((e) => e.isActive).length,
total: entities.length, total: entities.length,
icon: FaDatabase, icon: FaDatabase,
color: "text-blue-600", color: 'text-blue-600',
bgColor: "bg-blue-100", bgColor: 'bg-blue-100',
href: ROUTES_ENUM.protected.saas.developerKitEntities, href: ROUTES_ENUM.protected.saas.developerKit.entities,
}, },
{ {
name: translate('::App.DeveloperKit.Dashboard.Stats.Migrations'), name: translate('::App.DeveloperKit.Dashboard.Stats.Migrations'),
value: migrations.filter((m) => m.status === "pending").length, value: migrations.filter((m) => m.status === 'pending').length,
total: migrations.length, total: migrations.length,
icon: FaBolt, icon: FaBolt,
color: "text-yellow-600", color: 'text-yellow-600',
bgColor: "bg-yellow-100", bgColor: 'bg-yellow-100',
href: ROUTES_ENUM.protected.saas.developerKitMigrations, href: ROUTES_ENUM.protected.saas.developerKit.migrations,
}, },
{ {
name: translate('::App.DeveloperKit.Dashboard.Stats.APIs'), name: translate('::App.DeveloperKit.Dashboard.Stats.APIs'),
value: generatedEndpoints.filter((e) => e.isActive).length, value: generatedEndpoints.filter((e) => e.isActive).length,
total: generatedEndpoints.length, total: generatedEndpoints.length,
icon: FaServer, icon: FaServer,
color: "text-emerald-600", color: 'text-emerald-600',
bgColor: "bg-emerald-100", bgColor: 'bg-emerald-100',
href: ROUTES_ENUM.protected.saas.developerKitEndpoints, href: ROUTES_ENUM.protected.saas.developerKit.endpoints,
}, },
{ {
name: translate('::App.DeveloperKit.Dashboard.Stats.Components'), name: translate('::App.DeveloperKit.Dashboard.Stats.Components'),
value: components?.filter((c) => c.isActive).length, value: components?.filter((c) => c.isActive).length,
total: components?.length, total: components?.length,
icon: FaPuzzlePiece, icon: FaPuzzlePiece,
color: "text-purple-600", color: 'text-purple-600',
bgColor: "bg-purple-100", bgColor: 'bg-purple-100',
href: ROUTES_ENUM.protected.saas.developerKitComponents, href: ROUTES_ENUM.protected.saas.developerKit.components,
}, },
]; ]
const developmentFlow = [ const developmentFlow = [
{ {
@ -71,54 +71,50 @@ const Dashboard: React.FC = () => {
title: translate('::App.DeveloperKit.Dashboard.Flow.CreateEntity'), title: translate('::App.DeveloperKit.Dashboard.Flow.CreateEntity'),
description: translate('::App.DeveloperKit.Dashboard.Flow.CreateEntity.Desc'), description: translate('::App.DeveloperKit.Dashboard.Flow.CreateEntity.Desc'),
icon: FaDatabase, icon: FaDatabase,
color: "bg-blue-600", color: 'bg-blue-600',
href: ROUTES_ENUM.protected.saas.developerKitEntitiesNew, href: ROUTES_ENUM.protected.saas.developerKit.entitiesNew,
status: "ready", status: 'ready',
}, },
{ {
step: 2, step: 2,
title: translate('::App.DeveloperKit.Dashboard.Flow.GenerateMigration'), title: translate('::App.DeveloperKit.Dashboard.Flow.GenerateMigration'),
description: translate('::App.DeveloperKit.Dashboard.Flow.GenerateMigration.Desc'), description: translate('::App.DeveloperKit.Dashboard.Flow.GenerateMigration.Desc'),
icon: FaBolt, icon: FaBolt,
color: "bg-yellow-600", color: 'bg-yellow-600',
href: ROUTES_ENUM.protected.saas.developerKitMigrations, href: ROUTES_ENUM.protected.saas.developerKit.migrations,
status: entities.some((e) => e.migrationStatus === "pending") status: entities.some((e) => e.migrationStatus === 'pending') ? 'action-needed' : 'ready',
? "action-needed"
: "ready",
}, },
{ {
step: 3, step: 3,
title: translate('::App.DeveloperKit.Dashboard.Flow.ApplyMigration'), title: translate('::App.DeveloperKit.Dashboard.Flow.ApplyMigration'),
description: translate('::App.DeveloperKit.Dashboard.Flow.ApplyMigration.Desc'), description: translate('::App.DeveloperKit.Dashboard.Flow.ApplyMigration.Desc'),
icon: FaCheckCircle, icon: FaCheckCircle,
color: "bg-green-600", color: 'bg-green-600',
href: ROUTES_ENUM.protected.saas.developerKitMigrations, href: ROUTES_ENUM.protected.saas.developerKit.migrations,
status: migrations.some((m) => m.status === "pending") status: migrations.some((m) => m.status === 'pending') ? 'action-needed' : 'ready',
? "action-needed"
: "ready",
}, },
{ {
step: 4, step: 4,
title: translate('::App.DeveloperKit.Dashboard.Flow.GenerateAPI'), title: translate('::App.DeveloperKit.Dashboard.Flow.GenerateAPI'),
description: translate('::App.DeveloperKit.Dashboard.Flow.GenerateAPI.Desc'), description: translate('::App.DeveloperKit.Dashboard.Flow.GenerateAPI.Desc'),
icon: FaServer, icon: FaServer,
color: "bg-emerald-600", color: 'bg-emerald-600',
href: ROUTES_ENUM.protected.saas.developerKitEndpoints, href: ROUTES_ENUM.protected.saas.developerKit.endpoints,
status: "ready", status: 'ready',
}, },
{ {
step: 5, step: 5,
title: translate('::App.DeveloperKit.Dashboard.Flow.BuildComponent'), title: translate('::App.DeveloperKit.Dashboard.Flow.BuildComponent'),
description: translate('::App.DeveloperKit.Dashboard.Flow.BuildComponent.Desc'), description: translate('::App.DeveloperKit.Dashboard.Flow.BuildComponent.Desc'),
icon: FaPuzzlePiece, icon: FaPuzzlePiece,
color: "bg-purple-600", color: 'bg-purple-600',
href: ROUTES_ENUM.protected.saas.developerKitComponentsNew, href: ROUTES_ENUM.protected.saas.developerKit.componentsNew,
status: "ready", status: 'ready',
}, },
]; ]
const recentEntities = entities.slice(0, 3); const recentEntities = entities.slice(0, 3)
const recentMigrations = migrations.slice(0, 3); const recentMigrations = migrations.slice(0, 3)
const recentEndpoints = [ const recentEndpoints = [
...generatedEndpoints.map((e) => ({ ...generatedEndpoints.map((e) => ({
id: e.id, id: e.id,
@ -130,28 +126,36 @@ const Dashboard: React.FC = () => {
lastModificationTime: e.lastModificationTime, lastModificationTime: e.lastModificationTime,
creationTime: e.creationTime, creationTime: e.creationTime,
})), })),
].slice(0, 3); ].slice(0, 3)
const systemHealth = [ const systemHealth = [
{ name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Frontend'), status: translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy'), icon: FaCode }, {
name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Frontend'),
status: translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy'),
icon: FaCode,
},
{ {
name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Backend'), name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Backend'),
status: isOnline ? translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy') : translate('::App.DeveloperKit.Dashboard.SystemHealth.Offline'), status: isOnline
? translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy')
: translate('::App.DeveloperKit.Dashboard.SystemHealth.Offline'),
icon: FaServer, icon: FaServer,
}, },
{ {
name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Database'), name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Database'),
status: isOnline ? translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy') : translate('::App.DeveloperKit.Dashboard.SystemHealth.Unknown'), status: isOnline
? translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy')
: translate('::App.DeveloperKit.Dashboard.SystemHealth.Unknown'),
icon: FaDatabase, icon: FaDatabase,
}, },
{ {
name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Migrations'), name: translate('::App.DeveloperKit.Dashboard.SystemHealth.Migrations'),
status: migrations.some((m) => m.status === "failed") status: migrations.some((m) => m.status === 'failed')
? translate('::App.DeveloperKit.Dashboard.SystemHealth.Warning') ? translate('::App.DeveloperKit.Dashboard.SystemHealth.Warning')
: translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy'), : translate('::App.DeveloperKit.Dashboard.SystemHealth.Healthy'),
icon: FaBolt, icon: FaBolt,
}, },
]; ]
return ( return (
<div className="space-y-8"> <div className="space-y-8">
@ -161,25 +165,21 @@ const Dashboard: React.FC = () => {
<h1 className="text-3xl font-bold text-slate-900 mb-2"> <h1 className="text-3xl font-bold text-slate-900 mb-2">
{translate('::App.DeveloperKit.Dashboard.Title')} {translate('::App.DeveloperKit.Dashboard.Title')}
</h1> </h1>
<p className="text-slate-600"> <p className="text-slate-600">{translate('::App.DeveloperKit.Dashboard.Description')}</p>
{translate('::App.DeveloperKit.Dashboard.Description')}
</p>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<button <button
onClick={recheckHealth} onClick={recheckHealth}
className={`flex items-center gap-2 px-3 py-1 rounded-full text-sm font-medium transition-colors duration-200 ${ className={`flex items-center gap-2 px-3 py-1 rounded-full text-sm font-medium transition-colors duration-200 ${
isOnline isOnline
? "bg-green-100 text-green-700 hover:bg-green-200" ? 'bg-green-100 text-green-700 hover:bg-green-200'
: "bg-red-100 text-red-700 hover:bg-red-200" : 'bg-red-100 text-red-700 hover:bg-red-200'
}`} }`}
title={`Son kontrol: ${lastCheck.toLocaleTimeString()}`} title={`Son kontrol: ${lastCheck.toLocaleTimeString()}`}
> >
<div <div
className={`w-2 h-2 rounded-full ${ className={`w-2 h-2 rounded-full ${
isOnline isOnline ? 'bg-green-500 animate-pulse' : 'bg-red-500 animate-pulse'
? "bg-green-500 animate-pulse"
: "bg-red-500 animate-pulse"
}`} }`}
/> />
{isOnline ? ( {isOnline ? (
@ -200,7 +200,7 @@ const Dashboard: React.FC = () => {
{/* Stats Grid */} {/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{stats.map((stat) => { {stats.map((stat) => {
const Icon = stat.icon; const Icon = stat.icon
return ( return (
<Link <Link
key={stat.name} key={stat.name}
@ -216,18 +216,14 @@ const Dashboard: React.FC = () => {
<FaArrowRight className="w-4 h-4 text-slate-400 group-hover:text-slate-600 transition-colors" /> <FaArrowRight className="w-4 h-4 text-slate-400 group-hover:text-slate-600 transition-colors" />
</div> </div>
<div> <div>
<p className="text-sm font-medium text-slate-600"> <p className="text-sm font-medium text-slate-600">{stat.name}</p>
{stat.name}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1"> <p className="text-2xl font-bold text-slate-900 mt-1">
{stat.value} {stat.value}
<span className="text-sm font-normal text-slate-500 ml-1"> <span className="text-sm font-normal text-slate-500 ml-1">/ {stat.total}</span>
/ {stat.total}
</span>
</p> </p>
</div> </div>
</Link> </Link>
); )
})} })}
</div> </div>
@ -241,7 +237,7 @@ const Dashboard: React.FC = () => {
</div> </div>
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-5 gap-6">
{developmentFlow.map((flow, index) => { {developmentFlow.map((flow, index) => {
const Icon = flow.icon; const Icon = flow.icon
return ( return (
<Link key={flow.step} to={flow.href} className="group relative"> <Link key={flow.step} to={flow.href} className="group relative">
<div <div
@ -258,7 +254,7 @@ const Dashboard: React.FC = () => {
<h3 className="font-semibold text-lg mb-2 text-white">{flow.title}</h3> <h3 className="font-semibold text-lg mb-2 text-white">{flow.title}</h3>
<p className="text-sm opacity-90">{flow.description}</p> <p className="text-sm opacity-90">{flow.description}</p>
{flow.status === "action-needed" && ( {flow.status === 'action-needed' && (
<div className="absolute -top-2 -right-2"> <div className="absolute -top-2 -right-2">
<div className="bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center"> <div className="bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center">
<FaExclamationCircle className="w-4 h-4" /> <FaExclamationCircle className="w-4 h-4" />
@ -273,7 +269,7 @@ const Dashboard: React.FC = () => {
</div> </div>
)} )}
</Link> </Link>
); )
})} })}
</div> </div>
</div> </div>
@ -284,12 +280,12 @@ const Dashboard: React.FC = () => {
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4">
<FaCog className="w-5 h-5 text-green-500" /> <FaCog className="w-5 h-5 text-green-500" />
<h3 className="text-lg font-semibold text-slate-900"> <h3 className="text-lg font-semibold text-slate-900">
{translate('::App.DeveloperKit.Dashboard.SystemHealth.Title') } {translate('::App.DeveloperKit.Dashboard.SystemHealth.Title')}
</h3> </h3>
</div> </div>
<div className="space-y-3"> <div className="space-y-3">
{systemHealth.map((system) => { {systemHealth.map((system) => {
const Icon = system.icon; const Icon = system.icon
return ( return (
<div <div
key={system.name} key={system.name}
@ -297,38 +293,36 @@ const Dashboard: React.FC = () => {
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Icon className="w-4 h-4 text-slate-600" /> <Icon className="w-4 h-4 text-slate-600" />
<span className="font-medium text-slate-900"> <span className="font-medium text-slate-900">{system.name}</span>
{system.name}
</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className={`w-2 h-2 rounded-full ${ className={`w-2 h-2 rounded-full ${
system.status === "healthy" system.status === 'healthy'
? "bg-green-500" ? 'bg-green-500'
: system.status === "warning" : system.status === 'warning'
? "bg-yellow-500" ? 'bg-yellow-500'
: system.status === "offline" : system.status === 'offline'
? "bg-red-500" ? 'bg-red-500'
: "bg-gray-500" : 'bg-gray-500'
}`} }`}
/> />
<span <span
className={`text-sm capitalize ${ className={`text-sm capitalize ${
system.status === "healthy" system.status === 'healthy'
? "text-green-600" ? 'text-green-600'
: system.status === "warning" : system.status === 'warning'
? "text-yellow-600" ? 'text-yellow-600'
: system.status === "offline" : system.status === 'offline'
? "text-red-600" ? 'text-red-600'
: "text-gray-600" : 'text-gray-600'
}`} }`}
> >
{system.status === "offline" ? "Offline" : system.status} {system.status === 'offline' ? 'Offline' : system.status}
</span> </span>
</div> </div>
</div> </div>
); )
})} })}
</div> </div>
</div> </div>
@ -340,7 +334,7 @@ const Dashboard: React.FC = () => {
{translate('::App.DeveloperKit.Dashboard.RecentEntities.Title')} {translate('::App.DeveloperKit.Dashboard.RecentEntities.Title')}
</h3> </h3>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEntities} to={ROUTES_ENUM.protected.saas.developerKit.entities}
className="text-blue-600 hover:text-blue-700 text-sm font-medium" className="text-blue-600 hover:text-blue-700 text-sm font-medium"
> >
{translate('::App.DeveloperKit.Dashboard.ViewAll')} {translate('::App.DeveloperKit.Dashboard.ViewAll')}
@ -354,9 +348,7 @@ const Dashboard: React.FC = () => {
className="flex items-center justify-between p-3 bg-slate-50 rounded-lg hover:bg-slate-100 transition-colors" className="flex items-center justify-between p-3 bg-slate-50 rounded-lg hover:bg-slate-100 transition-colors"
> >
<div> <div>
<p className="font-medium text-slate-900"> <p className="font-medium text-slate-900">{entity.displayName}</p>
{entity.displayName}
</p>
<p className="text-sm text-slate-500"> <p className="text-sm text-slate-500">
{entity.fields.length} fields {entity.migrationStatus} {entity.fields.length} fields {entity.migrationStatus}
</p> </p>
@ -364,15 +356,18 @@ const Dashboard: React.FC = () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className={`w-2 h-2 rounded-full ${ className={`w-2 h-2 rounded-full ${
entity.migrationStatus === "applied" entity.migrationStatus === 'applied'
? "bg-green-500" ? 'bg-green-500'
: entity.migrationStatus === "pending" : entity.migrationStatus === 'pending'
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-red-500" : 'bg-red-500'
}`} }`}
/> />
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEntitiesEdit.replace(':id', entity.id)} to={ROUTES_ENUM.protected.saas.developerKit.entitiesEdit.replace(
':id',
entity.id,
)}
className="text-blue-600 hover:text-blue-700" className="text-blue-600 hover:text-blue-700"
> >
<FaDatabase className="w-4 h-4" /> <FaDatabase className="w-4 h-4" />
@ -383,9 +378,11 @@ const Dashboard: React.FC = () => {
) : ( ) : (
<div className="text-center py-8"> <div className="text-center py-8">
<FaDatabase className="w-12 h-12 text-slate-300 mx-auto mb-2" /> <FaDatabase className="w-12 h-12 text-slate-300 mx-auto mb-2" />
<p className="text-slate-500 mb-2">{translate('::App.DeveloperKit.Dashboard.Empty.Entity')}</p> <p className="text-slate-500 mb-2">
{translate('::App.DeveloperKit.Dashboard.Empty.Entity')}
</p>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEntitiesNew} to={ROUTES_ENUM.protected.saas.developerKit.entitiesNew}
className="text-blue-600 hover:text-blue-700 text-sm font-medium" className="text-blue-600 hover:text-blue-700 text-sm font-medium"
> >
Create your first entity Create your first entity
@ -402,7 +399,7 @@ const Dashboard: React.FC = () => {
{translate('::App.DeveloperKit.Dashboard.RecentMigrations.Title')} {translate('::App.DeveloperKit.Dashboard.RecentMigrations.Title')}
</h3> </h3>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitMigrations} to={ROUTES_ENUM.protected.saas.developerKit.migrations}
className="text-yellow-600 hover:text-yellow-700 text-sm font-medium" className="text-yellow-600 hover:text-yellow-700 text-sm font-medium"
> >
{translate('::App.DeveloperKit.Dashboard.ViewAll')} {translate('::App.DeveloperKit.Dashboard.ViewAll')}
@ -416,26 +413,23 @@ const Dashboard: React.FC = () => {
className="flex items-center justify-between p-3 bg-slate-50 rounded-lg hover:bg-slate-100 transition-colors" className="flex items-center justify-between p-3 bg-slate-50 rounded-lg hover:bg-slate-100 transition-colors"
> >
<div> <div>
<p className="font-medium text-slate-900"> <p className="font-medium text-slate-900">{migration.entityName}</p>
{migration.entityName}
</p>
<p className="text-sm text-slate-500"> <p className="text-sm text-slate-500">
{migration.status} {" "} {migration.status} {new Date(migration.creationTime).toLocaleDateString()}
{new Date(migration.creationTime).toLocaleDateString()}
</p> </p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className={`w-2 h-2 rounded-full ${ className={`w-2 h-2 rounded-full ${
migration.status === "applied" migration.status === 'applied'
? "bg-green-500" ? 'bg-green-500'
: migration.status === "pending" : migration.status === 'pending'
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-red-500" : 'bg-red-500'
}`} }`}
/> />
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitMigrations} to={ROUTES_ENUM.protected.saas.developerKit.migrations}
className="text-yellow-600 hover:text-yellow-700" className="text-yellow-600 hover:text-yellow-700"
> >
<FaBolt className="w-4 h-4" /> <FaBolt className="w-4 h-4" />
@ -446,9 +440,11 @@ const Dashboard: React.FC = () => {
) : ( ) : (
<div className="text-center py-8"> <div className="text-center py-8">
<FaBolt className="w-12 h-12 text-slate-300 mx-auto mb-2" /> <FaBolt className="w-12 h-12 text-slate-300 mx-auto mb-2" />
<p className="text-slate-500 mb-2">{translate('::App.DeveloperKit.Dashboard.Empty.Migration')}</p> <p className="text-slate-500 mb-2">
{translate('::App.DeveloperKit.Dashboard.Empty.Migration')}
</p>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEntitiesNew} to={ROUTES_ENUM.protected.saas.developerKit.entitiesNew}
className="text-yellow-600 hover:text-yellow-700 text-sm font-medium" className="text-yellow-600 hover:text-yellow-700 text-sm font-medium"
> >
{translate('::App.DeveloperKit.Dashboard.Action.GenerateMigrations')} {translate('::App.DeveloperKit.Dashboard.Action.GenerateMigrations')}
@ -465,7 +461,7 @@ const Dashboard: React.FC = () => {
{translate('::App.DeveloperKit.Dashboard.RecentEndpoints.Title')} {translate('::App.DeveloperKit.Dashboard.RecentEndpoints.Title')}
</h3> </h3>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEndpoints} to={ROUTES_ENUM.protected.saas.developerKit.endpoints}
className="text-emerald-600 hover:text-emerald-700 text-sm font-medium" className="text-emerald-600 hover:text-emerald-700 text-sm font-medium"
> >
{translate('::App.DeveloperKit.Dashboard.ViewAll')} {translate('::App.DeveloperKit.Dashboard.ViewAll')}
@ -482,33 +478,28 @@ const Dashboard: React.FC = () => {
<div className="flex items-center gap-2 mb-1"> <div className="flex items-center gap-2 mb-1">
<span <span
className={`px-2 py-0.5 text-xs font-medium rounded ${ className={`px-2 py-0.5 text-xs font-medium rounded ${
endpoint.method === "GET" endpoint.method === 'GET'
? "bg-blue-100 text-blue-800" ? 'bg-blue-100 text-blue-800'
: endpoint.method === "POST" : endpoint.method === 'POST'
? "bg-green-100 text-green-800" ? 'bg-green-100 text-green-800'
: endpoint.method === "PUT" : endpoint.method === 'PUT'
? "bg-yellow-100 text-yellow-800" ? 'bg-yellow-100 text-yellow-800'
: "bg-red-100 text-red-800" : 'bg-red-100 text-red-800'
}`} }`}
> >
{endpoint.method} {endpoint.method}
</span> </span>
<p className="font-medium text-slate-900 text-sm"> <p className="font-medium text-slate-900 text-sm">{endpoint.name}</p>
{endpoint.name}
</p>
</div> </div>
<p className="text-xs text-slate-500">{endpoint.path}</p> <p className="text-xs text-slate-500">{endpoint.path}</p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className={`w-2 h-2 rounded-full ${ className={`w-2 h-2 rounded-full ${
endpoint.isActive ? "bg-green-500" : "bg-slate-300" endpoint.isActive ? 'bg-green-500' : 'bg-slate-300'
}`} }`}
/> />
<Link <Link to="/docs" className="text-emerald-600 hover:text-emerald-700">
to="/docs"
className="text-emerald-600 hover:text-emerald-700"
>
<FaServer className="w-4 h-4" /> <FaServer className="w-4 h-4" />
</Link> </Link>
</div> </div>
@ -517,9 +508,11 @@ const Dashboard: React.FC = () => {
) : ( ) : (
<div className="text-center py-8"> <div className="text-center py-8">
<FaServer className="w-12 h-12 text-slate-300 mx-auto mb-2" /> <FaServer className="w-12 h-12 text-slate-300 mx-auto mb-2" />
<p className="text-slate-500 mb-2">{translate('::App.DeveloperKit.Dashboard.Empty.Endpoint')}</p> <p className="text-slate-500 mb-2">
{translate('::App.DeveloperKit.Dashboard.Empty.Endpoint')}
</p>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEndpointsNew} to={ROUTES_ENUM.protected.saas.developerKit.endpointsNew}
className="text-emerald-600 hover:text-emerald-700 text-sm font-medium" className="text-emerald-600 hover:text-emerald-700 text-sm font-medium"
> >
{translate('::App.DeveloperKit.Dashboard.Action.CreateEntity')} {translate('::App.DeveloperKit.Dashboard.Action.CreateEntity')}
@ -530,7 +523,7 @@ const Dashboard: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
}; }
export default Dashboard; export default Dashboard

View file

@ -7,8 +7,8 @@ import {
FaPlus, FaPlus,
FaTrashAlt, FaTrashAlt,
FaDatabase, FaDatabase,
FaQuestionCircle FaQuestionCircle,
} from 'react-icons/fa'; } from 'react-icons/fa'
import { CreateUpdateCustomEntityFieldDto, CustomEntityField } from '@/proxy/developerKit/models' import { CreateUpdateCustomEntityFieldDto, CustomEntityField } from '@/proxy/developerKit/models'
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'
@ -150,7 +150,7 @@ const EntityEditor: React.FC = () => {
addEntity(entityData) addEntity(entityData)
} }
navigate(ROUTES_ENUM.protected.saas.developerKitEntities) navigate(ROUTES_ENUM.protected.saas.developerKit.entities)
} catch (error) { } catch (error) {
console.error('Error saving entity:', error) console.error('Error saving entity:', error)
alert('Failed to save entity. Please try again.') alert('Failed to save entity. Please try again.')
@ -176,7 +176,7 @@ const EntityEditor: React.FC = () => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<button <button
onClick={() => navigate(ROUTES_ENUM.protected.saas.developerKitEntities)} onClick={() => navigate(ROUTES_ENUM.protected.saas.developerKit.entities)}
className="flex items-center gap-2 text-slate-600 hover:text-slate-900 transition-colors" className="flex items-center gap-2 text-slate-600 hover:text-slate-900 transition-colors"
> >
<FaArrowLeft className="w-4 h-4" /> <FaArrowLeft className="w-4 h-4" />
@ -202,7 +202,9 @@ const EntityEditor: React.FC = () => {
<div className="absolute -top-1 right-4 w-2 h-2 bg-slate-900 rotate-45"></div> <div className="absolute -top-1 right-4 w-2 h-2 bg-slate-900 rotate-45"></div>
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-2">
<FaDatabase className="w-4 h-4 text-blue-400" /> <FaDatabase className="w-4 h-4 text-blue-400" />
<h4 className="font-semibold text-blue-200">{translate('::App.DeveloperKit.EntityEditor.Tooltip.Title')}</h4> <h4 className="font-semibold text-blue-200">
{translate('::App.DeveloperKit.EntityEditor.Tooltip.Title')}
</h4>
</div> </div>
<p className="text-slate-300 leading-relaxed"> <p className="text-slate-300 leading-relaxed">
{translate('::App.DeveloperKit.EntityEditor.Tooltip.Content')} {translate('::App.DeveloperKit.EntityEditor.Tooltip.Content')}
@ -216,7 +218,9 @@ const EntityEditor: React.FC = () => {
className="flex items-center gap-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed shadow-sm" className="flex items-center gap-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
> >
<FaSave className="w-4 h-4" /> <FaSave className="w-4 h-4" />
{isSaving ? translate('::App.DeveloperKit.EntityEditor.Saving') : translate('::App.DeveloperKit.EntityEditor.Save')} {isSaving
? translate('::App.DeveloperKit.EntityEditor.Saving')
: translate('::App.DeveloperKit.EntityEditor.Save')}
</button> </button>
</div> </div>
</div> </div>
@ -224,10 +228,14 @@ const EntityEditor: React.FC = () => {
{/* Entity Basic Info */} {/* Entity Basic Info */}
<div className="bg-white rounded-lg border border-slate-200 p-6 mb-6 shadow-sm"> <div className="bg-white rounded-lg border border-slate-200 p-6 mb-6 shadow-sm">
<h2 className="text-lg font-semibold text-slate-900 mb-4">{translate('::App.DeveloperKit.EntityEditor.BasicInfo')}</h2> <h2 className="text-lg font-semibold text-slate-900 mb-4">
{translate('::App.DeveloperKit.EntityEditor.BasicInfo')}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div> <div>
<label className="block text-sm font-medium text-slate-700 mb-1">{translate('::App.DeveloperKit.EntityEditor.EntityName')}</label> <label className="block text-sm font-medium text-slate-700 mb-1">
{translate('::App.DeveloperKit.EntityEditor.EntityName')}
</label>
<input <input
autoFocus autoFocus
type="text" type="text"
@ -266,7 +274,9 @@ const EntityEditor: React.FC = () => {
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-slate-700 mb-1">{translate('::App.DeveloperKit.EntityEditor.TableName')} *</label> <label className="block text-sm font-medium text-slate-700 mb-1">
{translate('::App.DeveloperKit.EntityEditor.TableName')} *
</label>
<input <input
type="text" type="text"
value={tableName} value={tableName}
@ -284,7 +294,9 @@ const EntityEditor: React.FC = () => {
)} )}
</div> </div>
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm font-medium text-slate-700 mb-1">{translate('::App.DeveloperKit.EntityEditor.Description')}</label> <label className="block text-sm font-medium text-slate-700 mb-1">
{translate('::App.DeveloperKit.EntityEditor.Description')}
</label>
<input <input
type="text" type="text"
value={description} value={description}
@ -297,10 +309,14 @@ const EntityEditor: React.FC = () => {
{isEditing && ( {isEditing && (
<div className="mb-4 p-4 bg-slate-50 rounded-lg border border-slate-200"> <div className="mb-4 p-4 bg-slate-50 rounded-lg border border-slate-200">
<h3 className="text-sm font-medium text-slate-700 mb-3">{translate('::App.DeveloperKit.EntityEditor.Status')}</h3> <h3 className="text-sm font-medium text-slate-700 mb-3">
{translate('::App.DeveloperKit.EntityEditor.Status')}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm text-slate-600">{translate('::App.DeveloperKit.EntityEditor.Status.Migration')}</span> <span className="text-sm text-slate-600">
{translate('::App.DeveloperKit.EntityEditor.Status.Migration')}
</span>
<span <span
className={`text-xs px-2 py-1 rounded-full ${ className={`text-xs px-2 py-1 rounded-full ${
getEntity(id!)?.migrationStatus === 'applied' getEntity(id!)?.migrationStatus === 'applied'
@ -314,7 +330,9 @@ const EntityEditor: React.FC = () => {
</span> </span>
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm text-slate-600">{translate('::App.DeveloperKit.EntityEditor.Status.Endpoint')}</span> <span className="text-sm text-slate-600">
{translate('::App.DeveloperKit.EntityEditor.Status.Endpoint')}
</span>
<span <span
className={`text-xs px-2 py-1 rounded-full ${ className={`text-xs px-2 py-1 rounded-full ${
getEntity(id!)?.endpointStatus === 'applied' getEntity(id!)?.endpointStatus === 'applied'
@ -338,7 +356,9 @@ const EntityEditor: React.FC = () => {
onChange={(e) => setIsActive(e.target.checked)} onChange={(e) => setIsActive(e.target.checked)}
className="rounded border-slate-300 text-emerald-600 focus:ring-emerald-500" className="rounded border-slate-300 text-emerald-600 focus:ring-emerald-500"
/> />
<span className="text-sm font-medium text-slate-700">{translate('::App.DeveloperKit.EntityEditor.Active')}</span> <span className="text-sm font-medium text-slate-700">
{translate('::App.DeveloperKit.EntityEditor.Active')}
</span>
</label> </label>
<label className="flex items-center gap-2"> <label className="flex items-center gap-2">
<input <input
@ -372,7 +392,9 @@ const EntityEditor: React.FC = () => {
{/* Entity Fields */} {/* Entity Fields */}
<div className="bg-white rounded-lg border border-slate-200 p-6 shadow-sm"> <div className="bg-white rounded-lg border border-slate-200 p-6 shadow-sm">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-slate-900">{translate('::App.DeveloperKit.EntityEditor.Fields')}</h2> <h2 className="text-lg font-semibold text-slate-900">
{translate('::App.DeveloperKit.EntityEditor.Fields')}
</h2>
<button <button
onClick={addField} onClick={addField}
className="flex items-center gap-2 bg-blue-600 text-white px-3 py-2 rounded-lg hover:bg-blue-700 transition-colors text-sm" className="flex items-center gap-2 bg-blue-600 text-white px-3 py-2 rounded-lg hover:bg-blue-700 transition-colors text-sm"
@ -402,7 +424,9 @@ const EntityEditor: React.FC = () => {
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-slate-700 mb-1">{translate('::App.DeveloperKit.EntityEditor.Type')} *</label> <label className="block text-sm font-medium text-slate-700 mb-1">
{translate('::App.DeveloperKit.EntityEditor.Type')} *
</label>
<select <select
value={field.type} value={field.type}
onChange={(e) => onChange={(e) =>
@ -475,7 +499,9 @@ const EntityEditor: React.FC = () => {
} }
className="rounded border-slate-300 text-blue-600 focus:ring-blue-500" className="rounded border-slate-300 text-blue-600 focus:ring-blue-500"
/> />
<span className="text-sm font-medium text-slate-700">{translate('::App.DeveloperKit.EntityEditor.Required')}</span> <span className="text-sm font-medium text-slate-700">
{translate('::App.DeveloperKit.EntityEditor.Required')}
</span>
</label> </label>
<label className="flex items-center gap-2"> <label className="flex items-center gap-2">
<input <input
@ -484,7 +510,9 @@ const EntityEditor: React.FC = () => {
onChange={(e) => updateField(field.id, { isUnique: e.target.checked })} onChange={(e) => updateField(field.id, { isUnique: e.target.checked })}
className="rounded border-slate-300 text-blue-600 focus:ring-blue-500" className="rounded border-slate-300 text-blue-600 focus:ring-blue-500"
/> />
<span className="text-sm font-medium text-slate-700">{translate('::App.DeveloperKit.EntityEditor.Unique')}</span> <span className="text-sm font-medium text-slate-700">
{translate('::App.DeveloperKit.EntityEditor.Unique')}
</span>
</label> </label>
<button <button
onClick={() => removeField(field.id)} onClick={() => removeField(field.id)}
@ -503,7 +531,9 @@ const EntityEditor: React.FC = () => {
<div className="text-center py-8 text-slate-500"> <div className="text-center py-8 text-slate-500">
<FaDatabase className="w-12 h-12 mx-auto mb-2 text-slate-300" /> <FaDatabase className="w-12 h-12 mx-auto mb-2 text-slate-300" />
<p>{translate('::App.DeveloperKit.EntityEditor.NoFields')}</p> <p>{translate('::App.DeveloperKit.EntityEditor.NoFields')}</p>
<p className="text-sm">{translate('::App.DeveloperKit.EntityEditor.NoFieldsDescription')}</p> <p className="text-sm">
{translate('::App.DeveloperKit.EntityEditor.NoFieldsDescription')}
</p>
</div> </div>
)} )}
</div> </div>

View file

@ -1,6 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom'
import { useEntities } from '../../contexts/EntityContext'; import { useEntities } from '../../contexts/EntityContext'
import { import {
FaPlus, FaPlus,
FaSearch, FaSearch,
@ -13,66 +13,72 @@ import {
FaDatabase, FaDatabase,
FaCheckCircle, FaCheckCircle,
FaTable, FaTable,
FaBolt FaBolt,
} from 'react-icons/fa'; } from 'react-icons/fa'
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'
const EntityManager: React.FC = () => { const EntityManager: React.FC = () => {
const { entities, deleteEntity, toggleEntityActiveStatus } = useEntities(); const { entities, deleteEntity, toggleEntityActiveStatus } = useEntities()
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('')
const [filterActive, setFilterActive] = useState<'all' | 'active' | 'inactive'>('all'); const [filterActive, setFilterActive] = useState<'all' | 'active' | 'inactive'>('all')
const { translate } = useLocalization() const { translate } = useLocalization()
const filteredEntities = entities.filter(entity => { const filteredEntities = entities.filter((entity) => {
const matchesSearch = entity.name.toLowerCase().includes(searchTerm.toLowerCase()) || const matchesSearch =
entity.displayName.toLowerCase().includes(searchTerm.toLowerCase()) || entity.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
(entity.description || '').toLowerCase().includes(searchTerm.toLowerCase()); entity.displayName.toLowerCase().includes(searchTerm.toLowerCase()) ||
(entity.description || '').toLowerCase().includes(searchTerm.toLowerCase())
const matchesActiveFilter = filterActive === 'all' || const matchesActiveFilter =
(filterActive === 'active' && entity.isActive) || filterActive === 'all' ||
(filterActive === 'inactive' && !entity.isActive); (filterActive === 'active' && entity.isActive) ||
(filterActive === 'inactive' && !entity.isActive)
return matchesSearch && matchesActiveFilter; return matchesSearch && matchesActiveFilter
}); })
const handleToggleActive = async (id: string) => { const handleToggleActive = async (id: string) => {
try { try {
await toggleEntityActiveStatus(id); await toggleEntityActiveStatus(id)
} catch (err) { } catch (err) {
console.error('Failed to toggle entity status:', err); console.error('Failed to toggle entity status:', err)
} }
}; }
const handleDelete = async (id: string, name: string) => { const handleDelete = async (id: string, name: string) => {
if (window.confirm(`Are you sure you want to delete the "${name}" entity?\n\nThis action will permanently remove:\n• The entity and all its fields\n• All related migrations\n• All related API endpoints\n\nThis cannot be undone.`)) { if (
window.confirm(
`Are you sure you want to delete the "${name}" entity?\n\nThis action will permanently remove:\n• The entity and all its fields\n• All related migrations\n• All related API endpoints\n\nThis cannot be undone.`,
)
) {
try { try {
await deleteEntity(id); await deleteEntity(id)
} catch (err) { } catch (err) {
console.error('Failed to delete entity:', err); console.error('Failed to delete entity:', err)
alert(translate('::App.DeveloperKit.Entity.DeleteFailed')); alert(translate('::App.DeveloperKit.Entity.DeleteFailed'))
} }
} }
}; }
const stats = { const stats = {
total: entities.length, total: entities.length,
active: entities.filter(e => e.isActive).length, active: entities.filter((e) => e.isActive).length,
migrationsPending: entities.filter(e => e.migrationStatus === 'pending').length, migrationsPending: entities.filter((e) => e.migrationStatus === 'pending').length,
endpointsApplied: entities.filter(e => e.endpointStatus === 'applied').length endpointsApplied: entities.filter((e) => e.endpointStatus === 'applied').length,
}; }
return ( return (
<div className="space-y-8"> <div className="space-y-8">
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-8">
<div> <div>
<h1 className="text-3xl font-bold text-slate-900 mb-2">{translate('::App.DeveloperKit.Entity.Title')}</h1> <h1 className="text-3xl font-bold text-slate-900 mb-2">
<p className="text-slate-600"> {translate('::App.DeveloperKit.Entity.Title')}
{translate('::App.DeveloperKit.Entity.Description')} </h1>
</p> <p className="text-slate-600">{translate('::App.DeveloperKit.Entity.Description')}</p>
</div> </div>
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEntitiesNew} to={ROUTES_ENUM.protected.saas.developerKit.entitiesNew}
className="flex items-center gap-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors shadow-sm hover:shadow-md" className="flex items-center gap-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors shadow-sm hover:shadow-md"
> >
<FaPlus className="w-4 h-4" /> <FaPlus className="w-4 h-4" />
@ -85,7 +91,9 @@ const EntityManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Entity.TotalEntities')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Entity.TotalEntities')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1">{stats.total}</p> <p className="text-2xl font-bold text-slate-900 mt-1">{stats.total}</p>
</div> </div>
<div className="bg-blue-100 text-blue-600 p-3 rounded-lg"> <div className="bg-blue-100 text-blue-600 p-3 rounded-lg">
@ -96,12 +104,12 @@ const EntityManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Entity.ActiveEntities')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Entity.ActiveEntities')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1"> <p className="text-2xl font-bold text-slate-900 mt-1">
{stats.active} {stats.active}
<span className="text-sm font-normal text-slate-500 ml-1"> <span className="text-sm font-normal text-slate-500 ml-1">/ {stats.total}</span>
/ {stats.total}
</span>
</p> </p>
</div> </div>
<div className="bg-green-100 text-green-600 p-3 rounded-lg"> <div className="bg-green-100 text-green-600 p-3 rounded-lg">
@ -112,7 +120,9 @@ const EntityManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Entity.PendingMigrations')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Entity.PendingMigrations')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1">{stats.migrationsPending}</p> <p className="text-2xl font-bold text-slate-900 mt-1">{stats.migrationsPending}</p>
</div> </div>
<div className="bg-purple-100 text-purple-600 p-3 rounded-lg"> <div className="bg-purple-100 text-purple-600 p-3 rounded-lg">
@ -123,7 +133,9 @@ const EntityManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Entity.CrudEndpoints')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Entity.CrudEndpoints')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1">{stats.endpointsApplied}</p> <p className="text-2xl font-bold text-slate-900 mt-1">{stats.endpointsApplied}</p>
</div> </div>
<div className="bg-emerald-100 text-emerald-600 p-3 rounded-lg"> <div className="bg-emerald-100 text-emerald-600 p-3 rounded-lg">
@ -155,8 +167,12 @@ const EntityManager: React.FC = () => {
className="px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-transparent transition-colors" className="px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-transparent transition-colors"
> >
<option value="all">{translate('::App.DeveloperKit.Entity.Filter.All')}</option> <option value="all">{translate('::App.DeveloperKit.Entity.Filter.All')}</option>
<option value="active">{translate('::App.DeveloperKit.Entity.Filter.Active')}</option> <option value="active">
<option value="inactive">{translate('::App.DeveloperKit.Entity.Filter.Inactive')}</option> {translate('::App.DeveloperKit.Entity.Filter.Active')}
</option>
<option value="inactive">
{translate('::App.DeveloperKit.Entity.Filter.Inactive')}
</option>
</select> </select>
</div> </div>
</div> </div>
@ -168,7 +184,10 @@ const EntityManager: React.FC = () => {
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6"> <div className="grid grid-cols-1 xl:grid-cols-2 gap-6">
{filteredEntities.map((entity) => { {filteredEntities.map((entity) => {
return ( return (
<div key={entity.id} className="bg-white rounded-lg border border-slate-200 shadow-sm hover:shadow-md transition-all duration-200 group"> <div
key={entity.id}
className="bg-white rounded-lg border border-slate-200 shadow-sm hover:shadow-md transition-all duration-200 group"
>
<div className="p-6"> <div className="p-6">
<div className="flex items-start justify-between mb-4"> <div className="flex items-start justify-between mb-4">
<div className="flex-1"> <div className="flex-1">
@ -180,25 +199,29 @@ const EntityManager: React.FC = () => {
<h3 className="text-lg font-semibold text-slate-900"> <h3 className="text-lg font-semibold text-slate-900">
{entity.displayName} {entity.displayName}
</h3> </h3>
<p className="text-sm text-slate-500">{translate('::App.DeveloperKit.Entity.TableLabel')}: {entity.tableName}</p> <p className="text-sm text-slate-500">
{translate('::App.DeveloperKit.Entity.TableLabel')}: {entity.tableName}
</p>
</div> </div>
</div> </div>
{entity.description && ( {entity.description && (
<p className="text-slate-600 text-sm mb-3"> <p className="text-slate-600 text-sm mb-3">{entity.description}</p>
{entity.description}
</p>
)} )}
<div className="flex items-center gap-4 text-xs text-slate-500 mb-3"> <div className="flex items-center gap-4 text-xs text-slate-500 mb-3">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FaCalendarAlt className="w-3 h-3" /> <FaCalendarAlt className="w-3 h-3" />
<span> <span>
{translate('::App.DeveloperKit.Entity.Updated')}: {entity.lastModificationTime ? new Date(entity.lastModificationTime).toLocaleDateString() : 'Never'} {translate('::App.DeveloperKit.Entity.Updated')}:{' '}
{entity.lastModificationTime
? new Date(entity.lastModificationTime).toLocaleDateString()
: 'Never'}
</span> </span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FaDatabase className="w-3 h-3" /> <FaDatabase className="w-3 h-3" />
<span>{entity.fields.length} {translate('::App.DeveloperKit.Entity.Fields')}</span> <span>
{entity.fields.length} {translate('::App.DeveloperKit.Entity.Fields')}
</span>
</div> </div>
</div> </div>
</div> </div>
@ -207,17 +230,25 @@ const EntityManager: React.FC = () => {
{/* Entity Fields Preview */} {/* Entity Fields Preview */}
<div className="mb-4"> <div className="mb-4">
<div className="bg-slate-50 rounded-lg p-3"> <div className="bg-slate-50 rounded-lg p-3">
<h4 className="text-sm font-medium text-slate-700 mb-2">{translate('::App.DeveloperKit.Entity.FieldsLabel')}:</h4> <h4 className="text-sm font-medium text-slate-700 mb-2">
{translate('::App.DeveloperKit.Entity.FieldsLabel')}:
</h4>
<div className="grid grid-cols-2 gap-2 text-xs"> <div className="grid grid-cols-2 gap-2 text-xs">
{entity.fields.slice(0, 4).map(field => ( {entity.fields.slice(0, 4).map((field) => (
<div key={field.id} className="flex items-center gap-2"> <div key={field.id} className="flex items-center gap-2">
<span className={`px-2 py-1 rounded text-xs font-medium ${ <span
field.type === 'string' ? 'bg-blue-100 text-blue-700' : className={`px-2 py-1 rounded text-xs font-medium ${
field.type === 'number' || field.type === 'decimal' ? 'bg-green-100 text-green-700' : field.type === 'string'
field.type === 'boolean' ? 'bg-purple-100 text-purple-700' : ? 'bg-blue-100 text-blue-700'
field.type === 'date' ? 'bg-orange-100 text-orange-700' : : field.type === 'number' || field.type === 'decimal'
'bg-slate-100 text-slate-700' ? 'bg-green-100 text-green-700'
}`}> : field.type === 'boolean'
? 'bg-purple-100 text-purple-700'
: field.type === 'date'
? 'bg-orange-100 text-orange-700'
: 'bg-slate-100 text-slate-700'
}`}
>
{field.type} {field.type}
</span> </span>
<span className="text-slate-600">{field.name}</span> <span className="text-slate-600">{field.name}</span>
@ -226,7 +257,8 @@ const EntityManager: React.FC = () => {
))} ))}
{entity.fields.length > 4 && ( {entity.fields.length > 4 && (
<div className="text-slate-500 italic"> <div className="text-slate-500 italic">
+{entity.fields.length - 4} {translate('::App.DeveloperKit.Entity.More')} +{entity.fields.length - 4}{' '}
{translate('::App.DeveloperKit.Entity.More')}
</div> </div>
)} )}
</div> </div>
@ -236,22 +268,34 @@ const EntityManager: React.FC = () => {
{/* Migration and Endpoint Status */} {/* Migration and Endpoint Status */}
<div className="mb-4 space-y-3"> <div className="mb-4 space-y-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm font-medium text-slate-700">{translate('::App.DeveloperKit.Entity.MigrationStatus')}</span> <span className="text-sm font-medium text-slate-700">
<span className={`text-xs px-2 py-1 rounded-full ${ {translate('::App.DeveloperKit.Entity.MigrationStatus')}
entity.migrationStatus === 'applied' ? 'bg-green-100 text-green-700' : </span>
entity.migrationStatus === 'pending' ? 'bg-yellow-100 text-yellow-700' : <span
'bg-red-100 text-red-700' className={`text-xs px-2 py-1 rounded-full ${
}`}> entity.migrationStatus === 'applied'
? 'bg-green-100 text-green-700'
: entity.migrationStatus === 'pending'
? 'bg-yellow-100 text-yellow-700'
: 'bg-red-100 text-red-700'
}`}
>
{entity.migrationStatus} {entity.migrationStatus}
</span> </span>
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm font-medium text-slate-700">{translate('::App.DeveloperKit.Entity.EndpointStatus')}</span> <span className="text-sm font-medium text-slate-700">
<span className={`text-xs px-2 py-1 rounded-full ${ {translate('::App.DeveloperKit.Entity.EndpointStatus')}
entity.endpointStatus === 'applied' ? 'bg-blue-100 text-blue-700' : </span>
entity.endpointStatus === 'pending' ? 'bg-orange-100 text-orange-700' : <span
'bg-red-100 text-red-700' className={`text-xs px-2 py-1 rounded-full ${
}`}> entity.endpointStatus === 'applied'
? 'bg-blue-100 text-blue-700'
: entity.endpointStatus === 'pending'
? 'bg-orange-100 text-orange-700'
: 'bg-red-100 text-red-700'
}`}
>
{entity.endpointStatus} {entity.endpointStatus}
</span> </span>
</div> </div>
@ -283,7 +327,10 @@ const EntityManager: React.FC = () => {
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEntitiesEdit.replace(':id', entity.id)} to={ROUTES_ENUM.protected.saas.developerKit.entitiesEdit.replace(
':id',
entity.id,
)}
className="p-2 text-slate-600 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors" className="p-2 text-slate-600 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors"
title={translate('::App.DeveloperKit.Entity.Edit')} title={translate('::App.DeveloperKit.Entity.Edit')}
> >
@ -300,7 +347,7 @@ const EntityManager: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
})} })}
</div> </div>
) : ( ) : (
@ -310,15 +357,16 @@ const EntityManager: React.FC = () => {
<FaDatabase className="w-8 h-8 text-slate-500" /> <FaDatabase className="w-8 h-8 text-slate-500" />
</div> </div>
<h3 className="text-lg font-medium text-slate-900 mb-2"> <h3 className="text-lg font-medium text-slate-900 mb-2">
{searchTerm || filterActive !== 'all' ? translate('::App.DeveloperKit.Entity.NoEntitiesFound') : translate('::App.DeveloperKit.Entity.NoEntitiesYet')} {searchTerm || filterActive !== 'all'
? translate('::App.DeveloperKit.Entity.NoEntitiesFound')
: translate('::App.DeveloperKit.Entity.NoEntitiesYet')}
</h3> </h3>
<p className="text-slate-600 mb-6"> <p className="text-slate-600 mb-6">
{searchTerm || filterActive !== 'all' {searchTerm || filterActive !== 'all'
? translate('::App.DeveloperKit.Entity.TryAdjustingFilters') ? translate('::App.DeveloperKit.Entity.TryAdjustingFilters')
: translate('::App.DeveloperKit.Entity.CreateFirstEntity') : translate('::App.DeveloperKit.Entity.CreateFirstEntity')}
}
</p> </p>
{(!searchTerm && filterActive === 'all') && ( {!searchTerm && filterActive === 'all' && (
<Link <Link
to={ROUTES_ENUM.protected.saas.developerKitEntitiesNew} to={ROUTES_ENUM.protected.saas.developerKitEntitiesNew}
className="inline-flex items-center gap-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors shadow-sm hover:shadow-md" className="inline-flex items-center gap-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors shadow-sm hover:shadow-md"
@ -331,7 +379,7 @@ const EntityManager: React.FC = () => {
</div> </div>
)} )}
</div> </div>
); )
}; }
export default EntityManager; export default EntityManager

View file

@ -16,8 +16,8 @@ import {
FaEye, FaEye,
FaSync, FaSync,
FaTrashAlt, FaTrashAlt,
FaPlus FaPlus,
} from 'react-icons/fa'; } from 'react-icons/fa'
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'
@ -102,7 +102,7 @@ const MigrationManager: React.FC = () => {
setGeneratingEndpoints(entityId) setGeneratingEndpoints(entityId)
await generateCrudEndpoints(entityId) await generateCrudEndpoints(entityId)
// Navigate to API Endpoints page after successful generation // Navigate to API Endpoints page after successful generation
navigate(ROUTES_ENUM.protected.saas.developerKitMigrations) navigate(ROUTES_ENUM.protected.saas.developerKit.migrations)
} catch (error) { } catch (error) {
console.error('Failed to generate CRUD endpoints:', error) console.error('Failed to generate CRUD endpoints:', error)
} finally { } finally {
@ -186,7 +186,9 @@ const MigrationManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Migration.Total')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Migration.Total')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1">{stats.total}</p> <p className="text-2xl font-bold text-slate-900 mt-1">{stats.total}</p>
</div> </div>
<div className="bg-blue-100 text-blue-600 p-3 rounded-lg"> <div className="bg-blue-100 text-blue-600 p-3 rounded-lg">
@ -197,7 +199,9 @@ const MigrationManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Migration.Pending')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Migration.Pending')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1">{stats.pending}</p> <p className="text-2xl font-bold text-slate-900 mt-1">{stats.pending}</p>
</div> </div>
<div className="bg-yellow-100 text-yellow-600 p-3 rounded-lg"> <div className="bg-yellow-100 text-yellow-600 p-3 rounded-lg">
@ -208,7 +212,9 @@ const MigrationManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Migration.Applied')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Migration.Applied')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1">{stats.applied}</p> <p className="text-2xl font-bold text-slate-900 mt-1">{stats.applied}</p>
</div> </div>
<div className="bg-green-100 text-green-600 p-3 rounded-lg"> <div className="bg-green-100 text-green-600 p-3 rounded-lg">
@ -219,7 +225,9 @@ const MigrationManager: React.FC = () => {
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-slate-600">{translate('::App.DeveloperKit.Migration.Failed')}</p> <p className="text-sm font-medium text-slate-600">
{translate('::App.DeveloperKit.Migration.Failed')}
</p>
<p className="text-2xl font-bold text-slate-900 mt-1">{stats.failed}</p> <p className="text-2xl font-bold text-slate-900 mt-1">{stats.failed}</p>
</div> </div>
<div className="bg-red-100 text-red-600 p-3 rounded-lg"> <div className="bg-red-100 text-red-600 p-3 rounded-lg">
@ -249,14 +257,18 @@ const MigrationManager: React.FC = () => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h4 className="font-medium text-slate-900">{entity.displayName}</h4> <h4 className="font-medium text-slate-900">{entity.displayName}</h4>
<p className="text-sm text-slate-600">{entity.fields.length} {translate('::App.DeveloperKit.Migration.Fields')}</p> <p className="text-sm text-slate-600">
{entity.fields.length} {translate('::App.DeveloperKit.Migration.Fields')}
</p>
</div> </div>
<button <button
onClick={() => handleGenerateMigration(entity.id)} onClick={() => handleGenerateMigration(entity.id)}
disabled={loading} disabled={loading}
className="bg-yellow-600 text-white px-3 py-1 rounded text-sm hover:bg-yellow-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" className="bg-yellow-600 text-white px-3 py-1 rounded text-sm hover:bg-yellow-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
> >
{loading ? translate('::App.DeveloperKit.Migration.Generating') : translate('::App.DeveloperKit.Migration.Generate')} {loading
? translate('::App.DeveloperKit.Migration.Generating')
: translate('::App.DeveloperKit.Migration.Generate')}
</button> </button>
</div> </div>
</div> </div>
@ -334,14 +346,16 @@ const MigrationManager: React.FC = () => {
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FaCalendarAlt className="w-3 h-3" /> <FaCalendarAlt className="w-3 h-3" />
<span> <span>
{translate('::App.DeveloperKit.Migration.Created')} {new Date(migration.creationTime).toLocaleDateString()} {translate('::App.DeveloperKit.Migration.Created')}{' '}
{new Date(migration.creationTime).toLocaleDateString()}
</span> </span>
</div> </div>
{migration.appliedAt && ( {migration.appliedAt && (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FaCheckCircle className="w-3 h-3" /> <FaCheckCircle className="w-3 h-3" />
<span> <span>
{translate('::App.DeveloperKit.Migration.AppliedLabel')} {new Date(migration.appliedAt).toLocaleDateString()} {translate('::App.DeveloperKit.Migration.AppliedLabel')}{' '}
{new Date(migration.appliedAt).toLocaleDateString()}
</span> </span>
</div> </div>
)} )}
@ -367,7 +381,9 @@ const MigrationManager: React.FC = () => {
<FaEye className="w-4 h-4" /> <FaEye className="w-4 h-4" />
{translate('::App.DeveloperKit.Migration.ViewSQL')} {translate('::App.DeveloperKit.Migration.ViewSQL')}
{!migration.sqlScript && ( {!migration.sqlScript && (
<span className="ml-1 text-xs text-orange-500">({translate('::App.DeveloperKit.Migration.NoSQL')})</span> <span className="ml-1 text-xs text-orange-500">
({translate('::App.DeveloperKit.Migration.NoSQL')})
</span>
)} )}
</button> </button>
<button <button
@ -441,7 +457,9 @@ const MigrationManager: React.FC = () => {
{/* SQL Script Preview */} {/* SQL Script Preview */}
{selectedMigration === migration.id && ( {selectedMigration === migration.id && (
<div className="mt-4 pt-4 border-t border-slate-200"> <div className="mt-4 pt-4 border-t border-slate-200">
<h4 className="font-medium text-slate-900 mb-2">{translate('::App.DeveloperKit.Migration.SQLScriptLabel')}</h4> <h4 className="font-medium text-slate-900 mb-2">
{translate('::App.DeveloperKit.Migration.SQLScriptLabel')}
</h4>
{migration.sqlScript ? ( {migration.sqlScript ? (
<div className="bg-slate-900 rounded-lg p-4 overflow-x-auto"> <div className="bg-slate-900 rounded-lg p-4 overflow-x-auto">
<pre className="text-green-400 text-sm"> <pre className="text-green-400 text-sm">
@ -452,7 +470,9 @@ const MigrationManager: React.FC = () => {
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4"> <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<div className="flex items-center gap-2 text-yellow-800"> <div className="flex items-center gap-2 text-yellow-800">
<FaRegBell className="w-5 h-5" /> <FaRegBell className="w-5 h-5" />
<span className="font-medium">{translate('::App.DeveloperKit.Migration.NoSQLScript')}</span> <span className="font-medium">
{translate('::App.DeveloperKit.Migration.NoSQLScript')}
</span>
</div> </div>
<p className="text-yellow-700 text-sm mt-1"> <p className="text-yellow-700 text-sm mt-1">
{translate('::App.DeveloperKit.Migration.NoSQLScriptDesc')} {translate('::App.DeveloperKit.Migration.NoSQLScriptDesc')}
@ -468,7 +488,9 @@ const MigrationManager: React.FC = () => {
<div className="bg-red-50 border border-red-200 rounded-lg p-4"> <div className="bg-red-50 border border-red-200 rounded-lg p-4">
<div className="flex items-center gap-2 text-red-800 mb-2"> <div className="flex items-center gap-2 text-red-800 mb-2">
<FaRegBell className="w-5 h-5" /> <FaRegBell className="w-5 h-5" />
<span className="font-medium">{translate('::App.DeveloperKit.Migration.MigrationFailed')}</span> <span className="font-medium">
{translate('::App.DeveloperKit.Migration.MigrationFailed')}
</span>
</div> </div>
<p className="text-red-700 text-sm">{migration.errorMessage}</p> <p className="text-red-700 text-sm">{migration.errorMessage}</p>
</div> </div>
@ -486,7 +508,9 @@ const MigrationManager: React.FC = () => {
<FaBolt className="w-8 h-8 text-slate-500" /> <FaBolt className="w-8 h-8 text-slate-500" />
</div> </div>
<h3 className="text-lg font-medium text-slate-900 mb-2"> <h3 className="text-lg font-medium text-slate-900 mb-2">
{searchTerm || filterStatus !== 'all' ? translate('::App.DeveloperKit.Migration.NoMigrationsFound') : translate('::App.DeveloperKit.Migration.NoMigrationsYet')} {searchTerm || filterStatus !== 'all'
? translate('::App.DeveloperKit.Migration.NoMigrationsFound')
: translate('::App.DeveloperKit.Migration.NoMigrationsYet')}
</h3> </h3>
<p className="text-slate-600"> <p className="text-slate-600">
{searchTerm || filterStatus !== 'all' {searchTerm || filterStatus !== 'all'

View file

@ -19,31 +19,31 @@ const DeveloperLayout: React.FC<DeveloperLayoutProps> = ({ children }) => {
id: 'dashboard', id: 'dashboard',
label: translate('::App.DeveloperKit.Dashboard'), label: translate('::App.DeveloperKit.Dashboard'),
icon: FaTachometerAlt, icon: FaTachometerAlt,
path: ROUTES_ENUM.protected.saas.developerKit, path: ROUTES_ENUM.protected.saas.developerKit.dashboard,
}, },
{ {
id: 'entities', id: 'entities',
label: translate('::App.DeveloperKit.Entity'), label: translate('::App.DeveloperKit.Entity'),
icon: FaDatabase, icon: FaDatabase,
path: ROUTES_ENUM.protected.saas.developerKitEntities, path: ROUTES_ENUM.protected.saas.developerKit.entities,
}, },
{ {
id: 'migrations', id: 'migrations',
label: translate('::App.DeveloperKit.Migrations'), label: translate('::App.DeveloperKit.Migrations'),
icon: FaBolt, icon: FaBolt,
path: ROUTES_ENUM.protected.saas.developerKitMigrations, path: ROUTES_ENUM.protected.saas.developerKit.migrations,
}, },
{ {
id: 'endpoints', id: 'endpoints',
label: translate('::App.DeveloperKit.Endpoints'), label: translate('::App.DeveloperKit.Endpoints'),
icon: FaServer, icon: FaServer,
path: ROUTES_ENUM.protected.saas.developerKitEndpoints, path: ROUTES_ENUM.protected.saas.developerKit.endpoints,
}, },
{ {
id: 'components', id: 'components',
label: translate('::App.DeveloperKit.Components'), label: translate('::App.DeveloperKit.Components'),
icon: FaPuzzlePiece, icon: FaPuzzlePiece,
path: ROUTES_ENUM.protected.saas.developerKitComponents, path: ROUTES_ENUM.protected.saas.developerKit.components,
}, },
] ]

View file

@ -3,6 +3,7 @@ import { Button, Input, Dialog } from '../ui'
import { FaFileAlt } from 'react-icons/fa' import { FaFileAlt } from 'react-icons/fa'
import { ReportTemplateDto } from '@/proxy/reports/models' import { ReportTemplateDto } from '@/proxy/reports/models'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { ROUTES_ENUM } from '@/routes/route.constant'
interface ReportGeneratorProps { interface ReportGeneratorProps {
isOpen: boolean isOpen: boolean
@ -50,7 +51,7 @@ export const ReportGenerator: React.FC<ReportGeneratorProps> = ({
if (reportId) { if (reportId) {
// Yeni sekmede rapor URL'sini aç // Yeni sekmede rapor URL'sini aç
const reportUrl = `/admin/reports/${reportId}` const reportUrl = ROUTES_ENUM.protected.saas.reports.view.replace(':id', reportId)
window.open(reportUrl, '_blank') window.open(reportUrl, '_blank')
onClose() // Modal'ı kapat onClose() // Modal'ı kapat
} }

View file

@ -4,11 +4,12 @@ export const ROUTES_ENUM = {
about: '/about', about: '/about',
products: '/products', products: '/products',
checkout: '/checkout', checkout: '/checkout',
success: '/success',
payment: '/payment', payment: '/payment',
success: '/success',
services: '/services', services: '/services',
demo: '/demo',
blog: '/blog', blog: '/blog',
blogDetail: '/blog/:id',
demo: '/demo',
contact: '/contact', contact: '/contact',
accessDenied: '/access-denied', accessDenied: '/access-denied',
}, },
@ -33,17 +34,23 @@ export const ROUTES_ENUM = {
edit: '/admin/chart/edit/:chartCode', edit: '/admin/chart/edit/:chartCode',
}, },
forumManagement: '/admin/forumManagement', forumManagement: '/admin/forumManagement',
developerKit: '/admin/developerkit', developerKit: {
developerKitEntities: '/admin/developerkit/entities', dashboard: '/admin/developerkit',
developerKitEntitiesNew: '/admin/developerkit/entities/new', entities: '/admin/developerkit/entities',
developerKitEntitiesEdit: '/admin/developerkit/entities/edit/:id', entitiesNew: '/admin/developerkit/entities/new',
developerKitMigrations: '/admin/developerkit/migrations', entitiesEdit: '/admin/developerkit/entities/edit/:id',
developerKitEndpoints: '/admin/developerkit/endpoints', migrations: '/admin/developerkit/migrations',
developerKitEndpointsNew: '/admin/developerkit/endpoints/new', endpoints: '/admin/developerkit/endpoints',
developerKitComponents: '/admin/developerkit/components', endpointsNew: '/admin/developerkit/endpoints/new',
developerKitComponentsNew: '/admin/developerkit/components/new', components: '/admin/developerkit/components',
developerKitComponentsView: '/admin/developerkit/components/view/:id', componentsNew: '/admin/developerkit/components/new',
developerKitComponentsEdit: '/admin/developerkit/components/edit/:id', componentsView: '/admin/developerkit/components/view/:id',
componentsEdit: '/admin/developerkit/components/edit/:id',
},
reports: {
generator: '/admin/reports/generator',
view: '/admin/reports/:id',
},
}, },
admin: { admin: {
ai: '/admin/ai', ai: '/admin/ai',
@ -54,7 +61,6 @@ export const ROUTES_ENUM = {
}, },
activityLog: '/admin/activityLog', activityLog: '/admin/activityLog',
changeLog: '/admin/changeLog', changeLog: '/admin/changeLog',
settings: '/admin/settings', settings: '/admin/settings',
identity: { identity: {
user: { user: {

View file

@ -31,6 +31,7 @@ import { GridOptionsEditDto, ListFormCustomizationDto } from '@/proxy/form/model
import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models' import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models'
import { getListFormCustomizations } from '@/services/admin/list-form-customization.service' import { getListFormCustomizations } from '@/services/admin/list-form-customization.service'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { ROUTES_ENUM } from '@/routes/route.constant'
export interface FormEditProps { export interface FormEditProps {
onSubmit: ( onSubmit: (
@ -153,7 +154,7 @@ const FormEdit = () => {
<div className="lg:flex items-center justify-between mb-4 gap-3"> <div className="lg:flex items-center justify-between mb-4 gap-3">
<div className="mb-4 lg:mb-0"> <div className="mb-4 lg:mb-0">
<h4> <h4>
<Link to={`/admin/list/${listFormCode}`}> <Link to={ROUTES_ENUM.protected.admin.list.replace(':listFormCode', listFormCode)}>
🔙 {listFormCode} - {translate(`::${listFormValues.title}`)} 🔙 {listFormCode} - {translate(`::${listFormValues.title}`)}
</Link> </Link>
</h4> </h4>

View file

@ -68,6 +68,7 @@ import {
FaUserShield, FaUserShield,
} from 'react-icons/fa' } from 'react-icons/fa'
import { object, string } from 'yup' import { object, string } from 'yup'
import { ROUTES_ENUM } from '@/routes/route.constant'
const schema = object().shape({ const schema = object().shape({
id: string(), id: string(),
@ -587,7 +588,10 @@ const OrganizationUnits = () => {
</Td> </Td>
<Td> <Td>
<ActionLink <ActionLink
href={`/admin/identity/users/${user.id}/details`} href={ROUTES_ENUM.protected.admin.identity.user.detail.replace(
':userId',
user.id!,
)}
className="font-bold" className="font-bold"
target="_blank" target="_blank"
> >

View file

@ -21,6 +21,7 @@ import {
PermissionWithStyle, PermissionWithStyle,
UpdatePermissionDto, UpdatePermissionDto,
} from '@/proxy/admin/models' } from '@/proxy/admin/models'
import { ROUTES_ENUM } from '@/routes/route.constant'
const { Tr, Th, Td, THead, TBody } = Table const { Tr, Th, Td, THead, TBody } = Table
@ -249,7 +250,10 @@ const Users = () => {
</Td> </Td>
<Td> <Td>
<ActionLink <ActionLink
href={`/admin/identity/users/${user.id}/details`} href={ROUTES_ENUM.protected.admin.identity.user.detail.replace(
':userId',
user.id!,
)}
className="font-bold" className="font-bold"
> >
{user.name} {user.surname} {user.name} {user.surname}

View file

@ -4,6 +4,7 @@ import { useNavigate, useParams } from 'react-router-dom'
import useAccount from '@/utils/hooks/useAccount' import useAccount from '@/utils/hooks/useAccount'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { ROUTES_ENUM } from '@/routes/route.constant'
const VerifyConfirmationCode = () => { const VerifyConfirmationCode = () => {
const { userId, token } = useParams() const { userId, token } = useParams()
@ -37,7 +38,11 @@ const VerifyConfirmationCode = () => {
<Alert showIcon className="mb-4" type="danger"> <Alert showIcon className="mb-4" type="danger">
{error} {error}
</Alert> </Alert>
<Button block variant="solid" onClick={() => navigate('/account/confirm')}> <Button
block
variant="solid"
onClick={() => navigate(ROUTES_ENUM.authenticated.sendConfirmationCode)}
>
{translate('::Abp.Account.VerifyConfirmationCode')} {translate('::Abp.Account.VerifyConfirmationCode')}
</Button> </Button>
</div> </div>

View file

@ -15,6 +15,7 @@ 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' import { getAccessDeniedPath } from '@/utils/routing'
import { ROUTES_ENUM } from '@/routes/route.constant'
const useGridData = (props: { const useGridData = (props: {
mode: RowMode mode: RowMode
@ -125,7 +126,11 @@ const useGridData = (props: {
var result = await dataSource.update(id, data) var result = await dataSource.update(id, data)
if (result.data > 0) { if (result.data > 0) {
if (!isSubForm) { if (!isSubForm) {
navigate(`/form/${listFormCode}/${id}`) navigate(
ROUTES_ENUM.protected.admin.formView
.replace(':listFormCode', listFormCode)
.replace(':id', id!),
)
} else if (props.onSubmitAction) { } else if (props.onSubmitAction) {
props.onSubmitAction() props.onSubmitAction()
} }

View file

@ -14,6 +14,7 @@ import { setGridPanelColor } from './Utils'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { usePermission } from '@/utils/hooks/usePermission' import { usePermission } from '@/utils/hooks/usePermission'
import { usePWA } from '@/utils/hooks/usePWA' import { usePWA } from '@/utils/hooks/usePWA'
import { ROUTES_ENUM } from '@/routes/route.constant'
export interface ISelectBoxData { export interface ISelectBoxData {
value?: string value?: string
@ -177,9 +178,15 @@ const useFilters = ({
// Kayitli bir filtreyi silmek icin ilgili ekrani ac // Kayitli bir filtreyi silmek icin ilgili ekrani ac
setIsDeleteModalOpen(true) setIsDeleteModalOpen(true)
} else if (itemData.id === 'openPivotGrid') { } else if (itemData.id === 'openPivotGrid') {
navigate(`/pivot/${listFormCode}`) navigate(ROUTES_ENUM.protected.admin.pivot.replace(':listFormCode', listFormCode))
} else if (itemData.id === 'openManage') { } else if (itemData.id === 'openManage') {
window.open(`/admin/listform/edit/${listFormCode}`, isPwaMode ? '_self' : '_blank') window.open(
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(
':listFormCode',
listFormCode,
),
isPwaMode ? '_self' : '_blank',
)
} else if (itemData.id === 'clearFilter') { } else if (itemData.id === 'clearFilter') {
// gridin bütün filtrelerini temizle // gridin bütün filtrelerini temizle
const grid = gridRef.current?.instance const grid = gridRef.current?.instance