Button dönüşümleri

This commit is contained in:
Sedat ÖZTÜRK 2026-05-12 15:32:40 +03:00
parent bc31e1f06a
commit 99d64e95f1
22 changed files with 243 additions and 214 deletions

View file

@ -349,7 +349,7 @@
"CustomComponents": [
{
"name": "DynamicEntityComponent",
"code": "import React, { useEffect, useState } from \"react\";\nimport axios from \"axios\";\n\ninterface DynamicEntityComponentProps {\n title: string;\n}\n\nconst api = axios.create({\n baseURL: \"https://localhost:44344\", // defaults'ı her seferinde set etme\n});\n\nconst DynamicEntityComponent: React.FC<DynamicEntityComponentProps> = ({ title }) => {\n const [data, setData] = useState<Array<{ id: string; name: string }>>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const fetchData = async () => {\n setLoading(true);\n setError(null);\n\n try {\n const res = await api.get(`/api/app/crudendpoint/${title}`);\n const raw = Array.isArray(res.data) ? res.data : res.data?.items ?? [];\n\n const filtered = raw.map((item: any) => ({\n id: item.Id ?? item.id,\n name: item.Name ?? item.name,\n }));\n\n setData(filtered);\n } catch (err: any) {\n setError(err.message || \"Failed to fetch data\");\n } finally {\n setLoading(false);\n }\n };\n\n if (title) fetchData();\n }, [title]);\n\n if (loading) return <div>Loading...</div>;\n if (error) return <div className=\"text-red-600\">Error: {error}</div>;\n if (!data.length) return <div>No records found</div>;\n\n const headers = [\"id\", \"name\", \"actions\"];\n\n return (\n <div className=\"overflow-auto\">\n <table className=\"min-w-full bg-white border border-slate-200 shadow-sm rounded-lg\">\n <thead className=\"bg-slate-100\">\n <tr>\n {headers.map((key) => (\n <th\n key={key}\n className=\"text-left px-4 py-2 border-b border-slate-200 text-sm font-medium text-slate-700\"\n >\n {key === \"actions\" ? \"Actions\" : key}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.map((item, rowIndex) => (\n <tr key={rowIndex} className=\"hover:bg-slate-50\">\n <td className=\"px-4 py-2 border-b border-slate-100 text-sm text-slate-800\">\n {item.id}\n </td>\n <td className=\"px-4 py-2 border-b border-slate-100 text-sm text-slate-800\">\n {item.name}\n </td>\n <td className=\"px-4 py-2 border-b border-slate-100\">\n <button\n onClick={() => alert(item.name)}\n className=\"bg-blue-600 hover:bg-blue-700 text-white text-sm px-3 py-1 rounded-lg shadow-sm transition\"\n >\n Show Name\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n};\n\nexport default DynamicEntityComponent;",
"code": "import React, { useEffect, useState } from \"react\";\nimport axios from \"axios\";\n\ninterface DynamicEntityComponentProps {\n title: string;\n}\n\nconst api = axios.create({\n baseURL: \"https://localhost:44344\", // defaults'ı her seferinde set etme\n});\n\nconst DynamicEntityComponent: React.FC<DynamicEntityComponentProps> = ({ title }) => {\n const [data, setData] = useState<Array<{ id: string; name: string }>>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const fetchData = async () => {\n setLoading(true);\n setError(null);\n\n try {\n const res = await api.get(`/api/app/crudendpoint/${title}`);\n const raw = Array.isArray(res.data) ? res.data : res.data?.items ?? [];\n\n const filtered = raw.map((item: any) => ({\n id: item.Id ?? item.id,\n name: item.Name ?? item.name,\n }));\n\n setData(filtered);\n } catch (err: any) {\n setError(err.message || \"Failed to fetch data\");\n } finally {\n setLoading(false);\n }\n };\n\n if (title) fetchData();\n }, [title]);\n\n if (loading) return <div>Loading...</div>;\n if (error) return <div className=\"text-red-600\">Error: {error}</div>;\n if (!data.length) return <div>No records found</div>;\n\n const headers = [\"id\", \"name\", \"actions\"];\n\n return (\n <div className=\"overflow-auto\">\n <table className=\"min-w-full bg-white border border-slate-200 shadow-sm rounded-lg\">\n <thead className=\"bg-slate-100\">\n <tr>\n {headers.map((key) => (\n <th\n key={key}\n className=\"text-left px-4 py-2 border-b border-slate-200 text-sm font-medium text-slate-700\"\n >\n {key === \"actions\" ? \"Actions\" : key}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.map((item, rowIndex) => (\n <tr key={rowIndex} className=\"hover:bg-slate-50\">\n <td className=\"px-4 py-2 border-b border-slate-100 text-sm text-slate-800\">\n {item.id}\n </td>\n <td className=\"px-4 py-2 border-b border-slate-100 text-sm text-slate-800\">\n {item.name}\n </td>\n <td className=\"px-4 py-2 border-b border-slate-100\">\n <button\n type=\"button\"\n onClick={() => alert(item.name)}\n className=\"bg-blue-600 hover:bg-blue-700 text-white text-sm px-3 py-1 rounded-lg shadow-sm transition\"\n >\n Show Name\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n};\n\nexport default DynamicEntityComponent;",
"props": null,
"description": null,
"isActive": true,

View file

@ -197,9 +197,7 @@ const _Search = ({ className }: { className?: string }) => {
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
/>
</div>
<Button size="sm" onClick={handleSearch}>
<FaSearch />
</Button>
<Button icon={<FaSearch />} size="sm" onClick={handleSearch}></Button>
</div>
<div>
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-600">

View file

@ -33,7 +33,7 @@ const NotFoundPage = () => {
onClick={() =>
navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)
}
className="px-6 py-3 bg-blue-500 rounded-xl shadow hover:bg-blue-600 transition"
className="bg-blue-500 rounded-xl shadow hover:bg-blue-600 transition"
>
{translate('::Public.notFound.button')}
</Button>

View file

@ -4,7 +4,7 @@ import { Alert, Button } from '@/components/ui'
import { store, useStoreState } from '@/store'
import { Suspense, useEffect } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { FaArrowLeft } from 'react-icons/fa';
import { FaArrowLeft } from 'react-icons/fa'
import { Navigate, useLocation } from 'react-router-dom'
import DialogProvider from './shared/DialogContext'
import DialogShowComponent from './shared/DialogContext/DialogShowComponent'
@ -22,9 +22,13 @@ function fallbackRender({ error, resetErrorBoundary }: { error: Error; resetErro
<Alert showIcon className="mb-4" type="danger">
<h5>{error.name ?? 'Hata!'}</h5>
<div>{error.message}</div>
<Button size="sm" className="mt-2" variant="default" onClick={resetErrorBoundary}>
<FaArrowLeft />
</Button>
<Button
icon={<FaArrowLeft />}
size="sm"
className="mt-2"
variant="default"
onClick={resetErrorBoundary}
></Button>
</Alert>
)
}

View file

@ -20,6 +20,13 @@ const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>((props, ref) => {
{index > 0 && <FaChevronRight className="mx-2 h-4 w-4 text-gray-400" />}
<Button
size="sm"
icon={
index === 0 ? (
<FaHome className="h-4 w-4 mr-1" />
) : (
<FaFolder className="h-4 w-4 mr-1" />
)
}
onClick={() => onNavigate(item)}
className={classNames(
'flex items-center px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors',
@ -29,11 +36,6 @@ const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>((props, ref) => {
)}
disabled={index === items.length - 1}
>
{index === 0 ? (
<FaHome className="h-4 w-4 mr-1" />
) : (
<FaFolder className="h-4 w-4 mr-1" />
)}
<span className="truncate max-w-32">{item.name}</span>
</Button>
</div>

View file

@ -28,6 +28,7 @@ export interface MenuTreeNode {
displayName: string
icon?: string
url?: string
order?: number
children: MenuTreeNode[]
}
@ -42,6 +43,7 @@ export function buildMenuTree(items: MenuItem[]): MenuTreeNode[] {
displayName: item.displayName ?? item.code!,
icon: item.icon ?? undefined,
url: item.url ?? undefined,
order: item.order ?? undefined,
children: [],
})
})
@ -175,7 +177,7 @@ function TreeNode({
className={`text-xs shrink-0 ${isSelected ? 'text-indigo-200' : 'text-gray-400'}`}
onClick={() => !isEditing && onSelect(node.code)}
>
{translate('::' + node.displayName)}
{translate('::' + node.displayName)} ({node.order})
</span>
{isEditing ? (

View file

@ -431,23 +431,20 @@ const OrganizationUnits = () => {
<div className="file-actions">
<div className="flex gap-1 folderFileActions">
<Button
icon={<FaUserPlus size="20" color="#2d6da3" />}
onClick={() => setIsMoveAllUsersOpen(true)}
title={translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}
>
<FaUserPlus size="20" color="#2d6da3" />
</Button>
></Button>
<Button
icon={<FaEdit size="20" className="text-teal-900" />}
onClick={() => node.edit()}
title={translate('::Abp.Identity.OrganizationUnit.Rename')}
>
<FaEdit size="20" className="text-teal-900" />
</Button>
></Button>
<Button
icon={<FaTrashAlt size="20" color="#d9534f" />}
onClick={() => setDeleteRow({ id: node.data.id, name: 'Organization Unit' })}
title={translate('::Delete')}
>
<FaTrashAlt size="20" className="text-red-500" />
</Button>
></Button>
</div>
</div>
</div>
@ -472,6 +469,7 @@ const OrganizationUnits = () => {
<Button
variant="solid"
size="sm"
icon={activeOu ? <FaSitemap /> : <FaCogs />}
title={translate('::Abp.Identity.OrganizationUnit.AddUnit')}
onClick={(e) => {
e.preventDefault()
@ -480,21 +478,18 @@ const OrganizationUnits = () => {
displayName: '',
})
}}
>
{activeOu ? <FaSitemap /> : <FaCogs />}
</Button>
></Button>
<Button
variant="solid"
size="sm"
icon={<FaUsers />}
title={translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}
onClick={(e) => {
e.preventDefault()
setIsMoveAllUsersOpen(true)
}}
>
<FaUsers />
</Button>
></Button>
</div>
}
>
@ -548,14 +543,13 @@ const OrganizationUnits = () => {
<Button
variant="solid"
size="sm"
icon={<FaUserPlus />}
onClick={async (e) => {
e.preventDefault()
const response = await getUsers(0, 1000)
setUserSelectionList(response.data?.items ?? [])
}}
>
<FaUserPlus />
</Button>
></Button>
}
>
<Table compact>
@ -576,6 +570,7 @@ const OrganizationUnits = () => {
<Button
className="mr-auto"
type="button"
icon={<FaTrash />}
size="sm"
onClick={() => {
setDeleteRow({
@ -583,9 +578,7 @@ const OrganizationUnits = () => {
name: 'User',
})
}}
>
<FaTrash />
</Button>
></Button>
</Td>
<Td>
<ActionLink
@ -621,14 +614,13 @@ const OrganizationUnits = () => {
<Button
variant="solid"
size="sm"
icon={<FaUserPlus />}
onClick={async (e) => {
e.preventDefault()
const response = await getRoles(0, 1000)
setRoleSelectionList(response.data?.items ?? [])
}}
>
<FaUserPlus />
</Button>
></Button>
}
>
<Table compact>
@ -648,15 +640,14 @@ const OrganizationUnits = () => {
className="mr-auto"
type="button"
size="sm"
icon={<FaTrash />}
onClick={() => {
setDeleteRow({
id: role.id ?? '',
name: 'Role',
})
}}
>
<FaTrash />
</Button>
></Button>
</Td>
<Td>{role.name}</Td>
</Tr>

View file

@ -117,7 +117,6 @@ const NotificationSettings = () => {
checked={list[m][n].isActive}
onChange={(e) => onClickCheckbox(e, list[m][n])}
color={list[m][n].isCustomized ? 'red-500' : undefined}
title={list[m][n].id}
></Checkbox>
</Td>
))}

View file

@ -296,11 +296,11 @@ const RoomList: React.FC = () => {
{user.role === 'teacher' && (
<Button
size="sm"
icon={<FaPlus size={15} />}
variant="solid"
onClick={() => setShowCreateModal(true)}
className="flex items-center justify-center space-x-2 text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/20"
>
<FaPlus size={15} />
<span>{translate('::App.Videoroom.NewRoom')}</span>
</Button>
)}
@ -419,12 +419,12 @@ const RoomList: React.FC = () => {
size="sm"
variant="solid"
color="red-600"
icon={<FaPlay size={11} />}
className="flex items-center gap-1"
onClick={() => handlePlanningClass(classSession)}
disabled={!!classSession.actualStartTime}
title={translate('::App.Videoroom.Planning') || 'Planning'}
>
<FaUsers size={11} />
<span className="hidden sm:inline">
{translate('::App.Videoroom.Planning') || 'Planning'}
</span>
@ -434,12 +434,12 @@ const RoomList: React.FC = () => {
size="sm"
variant="solid"
color="orange-600"
icon={<FaEdit size={11} />}
className="flex items-center gap-1"
onClick={() => openEditModal(classSession)}
disabled={!!classSession.actualStartTime}
title={translate('::App.Platform.Edit')}
>
<FaEdit size={11} />
<span className="hidden sm:inline">
{translate('::App.Platform.Edit')}
</span>
@ -449,12 +449,13 @@ const RoomList: React.FC = () => {
size="sm"
variant="solid"
color="sky-800"
icon={<FaTrash size={11} />}
className="flex items-center gap-1"
onClick={() => openDeleteModal(classSession)}
disabled={!!classSession.actualStartTime}
title={translate('::App.Platform.Delete')}
>
<FaTrash size={11} />
<span className="hidden sm:inline">
{translate('::App.Platform.Delete')}
</span>
@ -465,12 +466,13 @@ const RoomList: React.FC = () => {
<Button
size="sm"
variant="solid"
icon={<FaPlay size={10} />}
onClick={event}
className="flex items-center gap-1"
color="emerald-600"
disabled={status === 'Katılıma Açık' ? true : false}
>
<FaPlay size={10} />
{title}
</Button>
</div>

View file

@ -346,12 +346,13 @@ export const NoteList: React.FC<NoteListProps> = ({
<Button
variant="solid"
size="sm"
icon={<FaPlus className="mr-1" />}
type="button"
onClick={onAddNote}
disabled={!onAddNote}
className="flex items-center"
>
<FaPlus className="mr-1" /> {translate('::ListForms.ListForm.AddNote')}
{translate('::ListForms.ListForm.AddNote')}
</Button>
</div>
@ -401,12 +402,11 @@ export const NoteList: React.FC<NoteListProps> = ({
<Button
variant="plain"
size="sm"
icon={<FaTrash />}
onClick={() => onDeleteNote?.(note.id as string)}
title={translate('::Delete')}
className="text-red-400 hover:text-red-600"
>
<FaTrash />
</Button>
></Button>
)}
</div>
@ -431,12 +431,11 @@ export const NoteList: React.FC<NoteListProps> = ({
<Button
variant="plain"
size="sm"
icon={<FaDownload />}
onClick={() => onDownloadFile?.(file)}
title={translate('::Download')}
className="text-blue-500 hover:text-blue-700 ml-1"
>
<FaDownload />
</Button>
></Button>
</div>
))}
</div>
@ -455,11 +454,12 @@ export const NoteList: React.FC<NoteListProps> = ({
size="sm"
variant="default"
type="button"
icon={<FaSyncAlt className="mr-1" />}
onClick={loadAuditLogs}
disabled={auditLoading}
className="flex items-center"
>
<FaSyncAlt className="mr-1" /> {translate('::ListForms.ListForm.Refresh')}
{translate('::ListForms.ListForm.Refresh')}
</Button>
</div>

View file

@ -70,7 +70,8 @@ export const NoteModal: React.FC<NoteModalProps> = ({
const beforeUpload = (files: FileList | null) => {
if (!files) return true
for (const f of Array.from(files)) {
if (f.size > 2 * 1024 * 1024) return translate('::ListForms.ListForm.NoteModal.Upload.MaxSize2Mb')
if (f.size > 2 * 1024 * 1024)
return translate('::ListForms.ListForm.NoteModal.Upload.MaxSize2Mb')
}
return true
}
@ -101,10 +102,7 @@ export const NoteModal: React.FC<NoteModalProps> = ({
<Form>
<FormContainer size="sm">
{/* NOT TİPİ */}
<FormItem
invalid={!!(errors.type && touched.type)}
errorMessage={errors.type}
>
<FormItem invalid={!!(errors.type && touched.type)} errorMessage={errors.type}>
<div className="flex gap-2">
{types.map((t) => (
<label
@ -246,12 +244,11 @@ export const NoteModal: React.FC<NoteModalProps> = ({
<Button
variant="plain"
size="sm"
icon={<FaTrash />}
type="button"
onClick={() => removeFile(index)}
className="text-red-500 hover:text-red-700"
>
<FaTrash />
</Button>
></Button>
</div>
))}
</div>

View file

@ -164,9 +164,13 @@ export const NotePanel: React.FC<NotePanelProps> = ({
</div>
<div className="flex items-center gap-3">
<Button variant="plain" size="sm" onClick={onToggle} title="Paneli kapat">
<FaTimes />
</Button>
<Button
variant="plain"
icon={<FaTimes />}
size="sm"
onClick={onToggle}
title="Paneli kapat"
></Button>
</div>
</div>

View file

@ -167,11 +167,11 @@ export function CategoryManagement({
</h2>
<Button
variant="solid"
icon={<FaPlus className="w-4 h-4" />}
onClick={() => setShowCreateForm(true)}
disabled={loading}
className="flex items-center space-x-2 bg-blue-600 px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
>
<FaPlus className="w-4 h-4" />
<span>{translate('::App.Forum.CategoryManagement.AddCategory')}</span>
</Button>
</div>
@ -369,7 +369,14 @@ export function CategoryManagement({
<div className="flex items-center space-x-2">
<Button
size='xs'
size="xs"
icon={
category.isActive ? (
<FaEye className="w-4 h-4" />
) : (
<FaEyeSlash className="w-4 h-4" />
)
}
onClick={() => handleToggleActive(category)}
className={`p-2 rounded-lg transition-colors ${
category.isActive
@ -377,16 +384,10 @@ export function CategoryManagement({
: 'text-red-600 hover:bg-red-100'
}`}
title={category.isActive ? 'Hide Category' : 'Show Category'}
>
{category.isActive ? (
<FaEye className="w-4 h-4" />
) : (
<FaEyeSlash className="w-4 h-4" />
)}
</Button>
></Button>
<Button
size='xs'
size="xs"
onClick={() => handleToggleLocked(category)}
className={`p-2 rounded-lg transition-colors ${
category.isLocked
@ -394,31 +395,30 @@ export function CategoryManagement({
: 'text-green-600 hover:bg-green-100'
}`}
title={category.isLocked ? 'Unlock Category' : 'Lock Category'}
>
{category.isLocked ? (
<FaLock className="w-4 h-4" />
) : (
<FaUnlock className="w-4 h-4" />
)}
</Button>
icon={
category.isLocked ? (
<FaLock className="w-4 h-4" />
) : (
<FaUnlock className="w-4 h-4" />
)
}
></Button>
<Button
size='xs'
size="xs"
onClick={() => handleEdit(category)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title={translate('::App.Forum.CategoryManagement.EditCategory')}
>
<FaEdit className="w-4 h-4" />
</Button>
icon={<FaEdit className="w-4 h-4" />}
></Button>
<Button
size='xs'
size="xs"
onClick={() => confirmDeleteCategory(category)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title={translate('::App.Forum.CategoryManagement.DeleteCategory')}
>
<FaTrash className="w-4 h-4" />
</Button>
icon={<FaTrash className="w-4 h-4" />}
></Button>
</div>
</div>
</div>

View file

@ -165,12 +165,12 @@ export function PostManagement({
{translate('::App.Forum.PostManagement.Title')}
</h2>
<Button
icon={<FaPlus className="w-4 h-4" />}
variant="solid"
onClick={() => setShowCreateForm(true)}
disabled={loading}
className="flex items-center space-x-2 bg-blue-600 px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
>
<FaPlus className="w-4 h-4" />
<span>{translate('::App.Forum.PostManagement.AddPost')}</span>
</Button>
</div>
@ -399,6 +399,13 @@ export function PostManagement({
<div className="flex items-center space-x-2 ml-4">
<Button
icon={
post.isAcceptedAnswer ? (
<FaCheckCircle className="w-4 h-4" />
) : (
<FaCircle className="w-4 h-4" />
)
}
onClick={() => handleToggleAcceptedAnswer(post)}
className={`p-2 rounded-lg transition-colors ${
post.isAcceptedAnswer
@ -410,28 +417,24 @@ export function PostManagement({
? 'Remove Accepted Answer'
: 'Mark as Accepted Answer'
}
>
{post.isAcceptedAnswer ? (
<FaCheckCircle className="w-4 h-4" />
) : (
<FaCircle className="w-4 h-4" />
)}
</Button>
></Button>
<Button
icon={<FaEdit className="w-4 h-4" />}
onClick={() => handleEdit(post)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title={translate('::App.Forum.PostManagement.EditPost')}
>
<FaEdit className="w-4 h-4" />
</Button>
<Button
icon={<FaTrashAlt className="w-4 h-4" />}
onClick={() => confirmDeletePost(post)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title={translate('::App.Forum.PostManagement.DeletePost')}
>
<FaTrashAlt className="w-4 h-4" />
</Button>
</div>
</div>

View file

@ -208,11 +208,11 @@ export function TopicManagement({
</h2>
<Button
variant="solid"
icon={<FaPlus className="w-4 h-4" />}
onClick={() => setShowCreateForm(true)}
disabled={loading}
className="flex items-center space-x-2 bg-blue-600 px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
>
<FaPlus className="w-4 h-4" />
<span>{translate('::App.Forum.TopicManagement.AddTopic')}</span>
</Button>
</div>
@ -400,6 +400,13 @@ export function TopicManagement({
<div className="flex items-center space-x-2 ml-4">
<Button
icon={
topic.isPinned ? (
<FaThumbtack className="w-4 h-4" />
) : (
<FaTree className="w-4 h-4" />
)
}
onClick={() => handlePin(topic)}
className={`p-2 rounded-lg transition-colors ${
topic.isPinned
@ -407,15 +414,16 @@ export function TopicManagement({
: 'text-gray-400 hover:bg-gray-100'
}`}
title={topic.isPinned ? 'Unpin Topic' : 'Pin Topic'}
>
{topic.isPinned ? (
<FaThumbtack className="w-4 h-4" />
) : (
<FaTree className="w-4 h-4" />
)}
</Button>
></Button>
<Button
icon={
topic.isLocked ? (
<FaLock className="w-4 h-4" />
) : (
<FaUnlock className="w-4 h-4" />
)
}
onClick={() => handleLock(topic)}
className={`p-2 rounded-lg transition-colors ${
topic.isLocked
@ -423,15 +431,16 @@ export function TopicManagement({
: 'text-green-600 hover:bg-green-100'
}`}
title={topic.isLocked ? 'Unlock Topic' : 'Lock Topic'}
>
{topic.isLocked ? (
<FaLock className="w-4 h-4" />
) : (
<FaUnlock className="w-4 h-4" />
)}
</Button>
></Button>
<Button
icon={
topic.isSolved ? (
<FaCheckCircle className="w-4 h-4" />
) : (
<FaCircle className="w-4 h-4" />
)
}
onClick={() => handleSolved(topic)}
className={`p-2 rounded-lg transition-colors ${
topic.isSolved
@ -439,29 +448,21 @@ export function TopicManagement({
: 'text-gray-400 hover:bg-gray-100'
}`}
title={topic.isSolved ? 'Mark as Unsolved' : 'Mark as Solved'}
>
{topic.isSolved ? (
<FaCheckCircle className="w-4 h-4" />
) : (
<FaCircle className="w-4 h-4" />
)}
</Button>
></Button>
<Button
icon={<FaEdit className="w-4 h-4" />}
onClick={() => handleEdit(topic)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title={translate('::App.Forum.TopicManagement.EditTopic')}
>
<FaEdit className="w-4 h-4" />
</Button>
></Button>
<Button
icon={<FaTrashAlt className="w-4 h-4" />}
onClick={() => confirmDeleteTopic(topic)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title={translate('::App.Forum.TopicManagement.DeleteTopic')}
>
<FaTrashAlt className="w-4 h-4" />
</Button>
></Button>
</div>
</div>
</div>

View file

@ -354,44 +354,43 @@ export function ForumView({
<div className="flex items-center space-x-2 ml-auto">
{viewState === 'topics' && selectedCategory && !selectedCategory.isLocked && (
<Button
variant='solid'
icon={<FaPlus className="w-4 h-4" />}
variant="solid"
onClick={() => setShowCreateTopic(true)}
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
>
<FaPlus className="w-4 h-4" />
<span>{translate('::App.Forum.TopicManagement.NewTopic')}</span>
</Button>
)}
{viewState === 'posts' && selectedTopic && !selectedTopic.isLocked && (
<Button
variant='solid'
icon={<FaPlus className="w-4 h-4" />}
variant="solid"
onClick={() => setShowCreatePost(true)}
className="flex items-center space-x-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors"
>
<FaPlus className="w-4 h-4" />
<span>{translate('::App.Forum.PostManagement.NewPost')}</span>
</Button>
)}
{/* Search */}
<Button
icon={<FaSearch className="w-4 h-4" />}
onClick={() => setIsSearchModalOpen(true)}
variant='default'
variant="default"
className="hidden md:flex items-center space-x-2 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
>
<FaSearch className="w-4 h-4 text-gray-400" />
<span className="text-gray-500">
{translate('::App.Forum.TopicManagement.Searchtopics')}
</span>
</Button>
<Button
icon={<FaSearch className="w-5 h-5" />}
onClick={() => setIsSearchModalOpen(true)}
variant='default'
variant="default"
className="md:hidden p-2 text-gray-400 hover:text-gray-600 transition-colors"
>
<FaSearch className="w-5 h-5" />
</Button>
></Button>
</div>
</div>

View file

@ -60,10 +60,10 @@ const Chart = (props: ChartProps) => {
const [openDrawer, setOpenDrawer] = useState(false)
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
// Ana state - Chart bu state'e göre render edilir
const [currentSeries, setCurrentSeries] = useState<ChartSeriesDto[]>([])
// Veritabanından gelen orijinal seriler (kaydetme işlemi için)
const [savedSeries, setSavedSeries] = useState<ChartSeriesDto[]>([])
@ -81,10 +81,10 @@ const Chart = (props: ChartProps) => {
const userSeriesData = initialSeries.filter((s) => s.userId === userName)
setAllSeries(initialSeries)
// Kullanıcının serisi varsa onu kullan, yoksa tüm serileri kullan
const seriesToUse = userSeriesData.length > 0 ? userSeriesData : initialSeries
// Sadece ilk yüklemede VEYA refresh sonrası güncelle
if (!initialized) {
setCurrentSeries(seriesToUse)
@ -128,9 +128,9 @@ const Chart = (props: ChartProps) => {
title: gridDto.gridOptions.titleDto,
size: gridDto.gridOptions.sizeDto?.useSize
? { width: gridDto.gridOptions.sizeDto.width, height: gridDto.gridOptions.sizeDto.height }
: {
width: openDrawer ? window.innerWidth - 550 : '100%',
height: window.innerHeight - 210
: {
width: openDrawer ? window.innerWidth - 550 : '100%',
height: window.innerHeight - 210,
},
legend: gridDto.gridOptions.legendDto || {
visible: true,
@ -167,7 +167,15 @@ const Chart = (props: ChartProps) => {
enabled: true,
},
}
}, [gridDto, currentSeries, initialized, createSelectDataSource, listFormCode, urlSearchParams, openDrawer])
}, [
gridDto,
currentSeries,
initialized,
createSelectDataSource,
listFormCode,
urlSearchParams,
openDrawer,
])
useEffect(() => {
if (memoizedChartOptions) {
@ -231,13 +239,15 @@ const Chart = (props: ChartProps) => {
})
if (resp.data?.items) {
const fieldNames = groupBy(resp?.data?.items, 'fieldName')
setFieldList(Object.keys(fieldNames).map((fieldName) => {
const firstItem = fieldNames[fieldName][0]
const label = firstItem.captionName
? translate('::' + firstItem.captionName)
: fieldName
return { value: fieldName, label }
}))
setFieldList(
Object.keys(fieldNames).map((fieldName) => {
const firstItem = fieldNames[fieldName][0]
const label = firstItem.captionName
? translate('::' + firstItem.captionName)
: fieldName
return { value: fieldName, label }
}),
)
}
} catch (error: any) {
toast.push(
@ -264,43 +274,48 @@ const Chart = (props: ChartProps) => {
setCurrentSeries(savedSeries)
}, [savedSeries])
const onSave = useCallback(async (newSeries: ChartSeriesDto[]) => {
// 1. Silinecek serileri bul (savedSeries var ama newSeries yok)
const toDelete = savedSeries.filter((old: ChartSeriesDto) => !newSeries.some((s) => s.index === old.index))
const onSave = useCallback(
async (newSeries: ChartSeriesDto[]) => {
// 1. Silinecek serileri bul (savedSeries var ama newSeries yok)
const toDelete = savedSeries.filter(
(old: ChartSeriesDto) => !newSeries.some((s) => s.index === old.index),
)
// Index kaymasını önlemek için büyükten küçüğe sırala
toDelete.sort((a: ChartSeriesDto, b: ChartSeriesDto) => b.index - a.index)
// Index kaymasını önlemek için büyükten küçüğe sırala
toDelete.sort((a: ChartSeriesDto, b: ChartSeriesDto) => b.index - a.index)
for (const old of toDelete) {
await deleteListFormJsonRow(id, ListFormEditTabs.ChartSeries.GeneralJsonRow, old.index)
}
// 2. Yeni veya güncellenen serileri kaydet
for (const series of newSeries) {
const input: ListFormJsonRowDto = {
index: series.index,
fieldName: ListFormEditTabs.ChartSeries.GeneralJsonRow,
itemChartSeries: series,
for (const old of toDelete) {
await deleteListFormJsonRow(id, ListFormEditTabs.ChartSeries.GeneralJsonRow, old.index)
}
if (series.index === -1) {
await postListFormJsonRow(id, input)
// 2. Yeni veya güncellenen serileri kaydet
for (const series of newSeries) {
const input: ListFormJsonRowDto = {
index: series.index,
fieldName: ListFormEditTabs.ChartSeries.GeneralJsonRow,
itemChartSeries: series,
}
if (series.index === -1) {
await postListFormJsonRow(id, input)
} else {
await putListFormJsonRow(id, input)
}
}
// 3. Yeniden yükle (veritabanından fresh data)
if (props.refreshGridDto) {
setInitialized(false)
await props.refreshGridDto()
// refreshGridDto tamamlandıktan sonra yeni state'ler otomatik setlenecek
} else {
await putListFormJsonRow(id, input)
// refreshGridDto yoksa manuel güncelle
setSavedSeries(newSeries)
setCurrentSeries(newSeries)
}
}
// 3. Yeniden yükle (veritabanından fresh data)
if (props.refreshGridDto) {
setInitialized(false)
await props.refreshGridDto()
// refreshGridDto tamamlandıktan sonra yeni state'ler otomatik setlenecek
} else {
// refreshGridDto yoksa manuel güncelle
setSavedSeries(newSeries)
setCurrentSeries(newSeries)
}
}, [savedSeries, id, props])
},
[savedSeries, id, props],
)
return (
<Container className={DX_CLASSNAMES}>
@ -343,28 +358,31 @@ const Chart = (props: ChartProps) => {
<Button
size="sm"
variant={'default'}
icon={<FaSyncAlt className="w-3 h-3" />}
className="text-sm flex items-center gap-1"
onClick={async () => {
setInitialized(false)
await refreshGridDto?.()
}}
title={translate('::App.Platform.Refresh')}
title={translate('::App.Platform.Refresh')}
>
<FaSyncAlt className="w-3 h-3" /> {translate('::App.Platform.Refresh')}
{translate('::App.Platform.Refresh')}
</Button>
<Button
size="sm"
icon={<FaCrosshairs className="w-3 h-3" />}
variant="default"
className="text-sm flex items-center gap-1"
onClick={() => setOpenDrawer(true)}
title={translate('::ListForms.ListFormEdit.TabChartSeries')}
>
<FaCrosshairs className="w-3 h-3" /> {translate('::ListForms.ListFormEdit.TabChartSeries')}
{translate('::ListForms.ListFormEdit.TabChartSeries')}
</Button>
{checkPermission(gridDto?.gridOptions.permissionDto.u) && (
<Button
size="sm"
icon={<FaCog className="w-3 h-3" />}
variant={'default'}
className="text-sm"
onClick={() => {
@ -377,13 +395,11 @@ const Chart = (props: ChartProps) => {
)
}}
title={translate('::ListForms.ListForm.Manage')}
>
<FaCog className="w-3 h-3" />
</Button>
></Button>
)}
</div>
</div>
<div
<div
className={`transition-all duration-300 ${openDrawer ? 'mr-[500px]' : 'mr-0'}`}
style={{ width: openDrawer ? 'calc(100% - 500px)' : '100%' }}
>

View file

@ -2,7 +2,10 @@ import { Button, FormContainer, Input, Notification, Select, toast } from '@/com
import { Field, FieldArray, Form, Formik, FieldProps } from 'formik'
import { FaMinus, FaPlus, FaTimes, FaSave } from 'react-icons/fa'
import { SelectBoxOption } from '@/types/shared'
import { chartSeriesTypeOptions, columnSummaryTypeListOptions } from '../admin/listForm/edit/options'
import {
chartSeriesTypeOptions,
columnSummaryTypeListOptions,
} from '../admin/listForm/edit/options'
import { ChartSeriesDto } from '@/proxy/admin/charts/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { useStoreState } from '@/store/store'
@ -144,12 +147,11 @@ const ChartDrawer = ({
{translate('::App.Platform.ChartDrawer.ChartSeries')}
</h2>
<Button
icon={<FaTimes />}
type="button"
onClick={onClose}
className="p-2 hover:bg-gray-200 rounded-full transition-colors"
>
<FaTimes />
</Button>
></Button>
</div>
<FormContainer size="sm" className="flex flex-col flex-1 overflow-hidden">
@ -159,13 +161,14 @@ const ChartDrawer = ({
variant="solid"
type="button"
size="sm"
icon={<FaPlus />}
onClick={() => {
setFieldValue('series', [...values.series, newSeriesValue()])
}}
className="w-full"
>
<div className="flex items-center justify-center gap-2">
<FaPlus /> {translate('::App.Platform.ChartDrawer.AddNewSeries')}
{translate('::App.Platform.ChartDrawer.AddNewSeries')}
</div>
</Button>
</div>
@ -215,12 +218,14 @@ const ChartDrawer = ({
className="w-full px-3 py-2 text-left border rounded hover:bg-white flex items-center gap-2 transition-colors"
>
<span className="text-xl">
{chartSeriesTypeOptions.find((t) => t.label === field.value)
?.icon || '📊'}
{chartSeriesTypeOptions.find(
(t) => t.label === field.value,
)?.icon || '📊'}
</span>
<span className="text-sm font-medium">
{chartSeriesTypeOptions.find((t) => t.label === field.value)
?.label || field.value}
{chartSeriesTypeOptions.find(
(t) => t.label === field.value,
)?.label || field.value}
</span>
</Button>
{selectedSeriesIndex === index && (
@ -354,9 +359,14 @@ const ChartDrawer = ({
<Button type="button" variant="plain" onClick={onClose} className="flex-1">
{translate('::Cancel')}
</Button>
<Button variant="solid" loading={isSubmitting} type="submit" className="flex-1">
<Button
icon={<FaSave />}
variant="solid"
loading={isSubmitting}
type="submit"
className="flex-1"
>
<div className="flex items-center justify-center gap-2">
<FaSave />
{isSubmitting
? translate('::SavingWithThreeDot')
: translate('::App.Platform.ChartDrawer.Save')}

View file

@ -161,11 +161,11 @@ const List: React.FC = () => {
gridDto?.gridOptions?.schedulerOptionDto?.startDateExpr && (
<Button
size="sm"
icon={<FaCalendarAlt />}
variant={viewMode === 'scheduler' ? 'solid' : 'default'}
onClick={() => setLayout('scheduler')}
onMouseEnter={() => preload.scheduler()}
>
<FaCalendarAlt />
</Button>
)}
@ -174,11 +174,11 @@ const List: React.FC = () => {
gridDto?.gridOptions?.ganttOptionDto?.titleExpr && (
<Button
size="sm"
icon={<FaProjectDiagram />}
variant={viewMode === 'gantt' ? 'solid' : 'default'}
onClick={() => setLayout('gantt')}
onMouseEnter={() => preload.gantt()}
>
<FaProjectDiagram />
</Button>
)}
@ -186,42 +186,42 @@ const List: React.FC = () => {
gridDto?.gridOptions?.treeOptionDto?.parentIdExpr && (
<Button
size="sm"
icon={<FaSitemap />}
variant={viewMode === 'tree' ? 'solid' : 'default'}
onClick={() => setLayout('tree')}
onMouseEnter={() => preload.tree()}
>
<FaSitemap />
</Button>
)}
<Button
size="sm"
icon={<FaList />}
variant={viewMode === 'grid' ? 'solid' : 'default'}
onClick={() => setLayout('grid')}
onMouseEnter={() => preload.grid()}
>
<FaList />
</Button>
{gridDto.gridOptions.layoutDto.pivot && (
<Button
size="sm"
icon={<FaTable />}
variant={viewMode === 'pivot' ? 'solid' : 'default'}
onClick={() => setLayout('pivot')}
onMouseEnter={() => preload.pivot()}
>
<FaTable />
</Button>
)}
{gridDto.gridOptions.layoutDto.chart && (
<Button
size="sm"
icon={<FaChartArea />}
variant={viewMode === 'chart' ? 'solid' : 'default'}
onClick={() => setLayout('chart')}
onMouseEnter={() => preload.chart()}
>
<FaChartArea />
</Button>
)}
</div>

View file

@ -434,6 +434,7 @@ const Pivot = (props: PivotProps) => {
<Button
size="sm"
variant={'plain'}
icon={<FaCaretDown className="ml-2 w-2 h-3" />}
className="text-sm flex items-center gap-1 border-0"
title="Menu"
>
@ -441,7 +442,6 @@ const Pivot = (props: PivotProps) => {
<span className="text-black dark:text-white" style={{ fontSize: '13px' }}>
{translate('::ListForms.ListForm.GridMenu')}
</span>
<FaCaretDown className="ml-2 w-2 h-3" />
</Button>
}
>

View file

@ -13,7 +13,7 @@ const NotFound: React.FC = () => {
<p className="text-xl text-gray-600 mb-8 text-center max-w-md"> {/* Metin rengi, margin ve max-width güncellendi */}
{translate('::Public.notFound.message')}
</p>
<a href={ROUTES_ENUM.public.home} className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition duration-300 text-lg font-semibold"> {/* Buton stili güncellendi */}
<a href={ROUTES_ENUM.public.home} className="bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition duration-300 text-lg font-semibold"> {/* Buton stili güncellendi */}
{translate('::Public.notFound.button')}
</a>
</div>

View file

@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react'
import { APP_NAME } from '@/constants/app.constant'
import { getMigrateUrl, getSetupStatus } from '@/services/setup.service'
import { applicationConfigurationUrl } from '@/services/abpConfig.service'
import { Button } from '@/components/ui'
interface LogLine {
level: 'info' | 'warn' | 'error' | 'success' | 'restart' | 'done'
@ -248,12 +249,12 @@ const DatabaseSetup = () => {
<div className="flex gap-3">
{(status === 'idle' || status === 'error') && !dbExists && (
<button
<Button
onClick={startMigration}
className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-sm font-medium rounded-lg transition-colors"
>
{status === 'error' ? 'Retry' : 'Start Setup'}
</button>
</Button>
)}
{status === 'restarting' && (