From 90a66562e7aaf6c9bcafd3359ec747d22a7b41a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Thu, 20 Aug 2026 18:20:49 +0300 Subject: [PATCH] =?UTF-8?q?Wizard=20ve=20Component=20Manager=20List=20ve?= =?UTF-8?q?=20Card=20g=C3=B6r=C3=BCn=C3=BCmleri?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Migrations/LanguagesData.json | 42 ++ .../listForm/wizard/WizardFileManager.tsx | 354 ++++++++++------ .../views/developerKit/ComponentManager.tsx | 379 ++++++++++++------ .../views/forum/admin/CategoryManagement.tsx | 54 +-- ui/src/views/forum/admin/PostManagement.tsx | 49 ++- ui/src/views/forum/admin/TopicManagement.tsx | 57 +-- 6 files changed, 610 insertions(+), 325 deletions(-) diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json index 5f3698fe..3d01bad9 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json @@ -10854,6 +10854,48 @@ "tr": "Görüntüle", "en": "View" }, + { + "resourceName": "Platform", + "key": "App.Platform.CardView", + "tr": "Kart Görünümü", + "en": "Card View" + }, + { + "resourceName": "Platform", + "key": "App.Platform.ListView", + "tr": "Liste Görünümü", + "en": "List View" + }, + { + "resourceName": "Platform", + "key": "App.Platform.Name", + "tr": "Ad", + "en": "Name" + }, + { + "resourceName": "Platform", + "key": "App.Platform.Description", + "tr": "Açıklama", + "en": "Description" + }, + { + "resourceName": "Platform", + "key": "App.Platform.Actions", + "tr": "İşlemler", + "en": "Actions" + }, + { + "resourceName": "Platform", + "key": "App.Platform.Dependencies", + "tr": "Bağımlılıklar", + "en": "Dependencies" + }, + { + "resourceName": "Platform", + "key": "App.Platform.RoutePath", + "tr": "Rota", + "en": "Route Path" + }, { "resourceName": "Platform", "key": "App.Platform.Manage2", diff --git a/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx b/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx index df077ff5..211234c2 100644 --- a/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx +++ b/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx @@ -18,6 +18,8 @@ import { FaExternalLinkAlt, FaFileExport, FaFileImport, + FaTh, + FaList, } from 'react-icons/fa' import DbMigrateButton from '@/components/shared/DbMigrateButton' import { @@ -28,7 +30,8 @@ import { } from '@/services/wizard.service' import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' import { useLocalization } from '@/utils/hooks/useLocalization' -import { useStoreState } from '@/store/store' +import { useStoreActions, useStoreState } from '@/store/store' +import type { ListViewLayoutType } from '../edit/types' import { ROUTES_ENUM } from '@/routes/route.constant' import { WizardComponentKindEnum, WizardFileInfoDto } from '@/proxy/admin/wizard/models' import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config' @@ -36,6 +39,9 @@ import { usePermission } from '@/utils/hooks/usePermission' import { WIZARD_PERMISSION } from '@/constants/permission.constant' import WizardImportDialog from './WizardImportDialog' +/** Layout tercihi diger listelerle ayni yerde (admin.lists.states) saklanir. */ +const VIEW_STATE_CODE = 'developerkit-wizardmanager' + interface ConfirmState { fileName: string wizardName: string @@ -57,6 +63,13 @@ const WizardFileManager = () => { const [importFile, setImportFile] = useState(null) const importInputRef = useRef(null) + const { states } = useStoreState((state) => state.admin.lists) + const { setStates } = useStoreActions((a) => a.admin.lists) + const viewMode: ListViewLayoutType = + states.find((s) => s.listFormCode === VIEW_STATE_CODE)?.layout === 'grid' ? 'grid' : 'card' + const setViewMode = (layout: ListViewLayoutType) => + setStates({ listFormCode: VIEW_STATE_CODE, layout }) + // Butonlar yalnizca gizlenir; asil kontrol ListFormWizardAppService'te yapilir. const { checkPermission } = usePermission() const canCreate = checkPermission(WIZARD_PERMISSION.CREATE) @@ -152,6 +165,72 @@ const WizardFileManager = () => { } } + const renderActions = (f: WizardFileInfoDto, className: string) => ( +
+ {!f.hasInsertedRecords && ( + + + + )} + {canUpdate && ( + + )} + {/* Menünün açtığı adres yeni sekmede açılır; adres yoksa buton anlamsızdır. */} + {f.menuUrl && ( + + )} + {canExport && ( + + )} + {canDelete && ( + + )} +
+ ) + return ( {/* ── Header ─────────────────────────────────────────────── */} @@ -227,6 +306,24 @@ const WizardFileManager = () => { )} +
+
@@ -246,138 +343,149 @@ const WizardFileManager = () => {

)} -
- {filteredFiles.map((f) => { - const Icon = navigationIcon[f.menuIcon] ?? FaDatabase - const isCustom = f.componentKind === WizardComponentKindEnum.Custom - return ( -
-
- -
- {/* 1. satır: wizard adı */} -
- {f.wizardName || f.fileName} -
- - {/* 2. satır: dosya adı ve üretilen kod */} -
- {f.fileName} - - - {isCustom ? f.customComponentName : f.listFormCode} - -
- - {/* 3. satır: yol ayrımı (Custom / List), layout ve menü adresi */} -
- + {filteredFiles.map((f) => { + const Icon = navigationIcon[f.menuIcon] ?? FaDatabase + const isCustom = f.componentKind === WizardComponentKindEnum.Custom + return ( +
+
+ +
+ {/* 1. satır: wizard adı */} +
- {isCustom ? : } - {translate( - isCustom - ? '::App.WizardStepComponent.CustomComponent' - : '::App.WizardStepComponent.ListComponent', - )} - + {f.wizardName || f.fileName} +
- {/* Custom yolunda layout kavramı yoktur. */} - {!isCustom && f.defaultLayout && ( - - {f.defaultLayout} - - )} - - {f.menuUrl && ( - - - {f.menuUrl} - - )} + {/* 2. satır: dosya adı ve üretilen kod */} +
+ {f.fileName} +
+
+ {isCustom ? f.customComponentName : f.listFormCode} +
-
-
- {!f.hasInsertedRecords && ( + {/* 3. satır: yol ayrımı (Custom / List), layout ve menü adresi */} +
- + {isCustom ? : } + {translate( + isCustom + ? '::App.WizardStepComponent.CustomComponent' + : '::App.WizardStepComponent.ListComponent', + )} - )} - {/* Menünün açtığı adres yeni sekmede açılır; adres yoksa buton anlamsızdır. */} - {f.menuUrl && ( - - )} - {canExport && ( - - )} - {canUpdate && ( - - )} - {canDelete && ( - + + {/* Custom yolunda layout kavramı yoktur. */} + {!isCustom && f.defaultLayout && ( + + {f.defaultLayout} + + )} + + {f.menuUrl && ( + + + {f.menuUrl} + + )} +
+ + {renderActions( + f, + 'flex items-center justify-end gap-1 pt-1 border-t border-gray-200 dark:border-gray-700', )}
-
- ) - })} -
+ ) + })} +
+ ) : ( +
+ {filteredFiles.map((f) => { + const Icon = navigationIcon[f.menuIcon] ?? FaDatabase + const isCustom = f.componentKind === WizardComponentKindEnum.Custom + return ( +
+
+ +
+ {/* 1. satır: wizard adı */} +
+ {f.wizardName || f.fileName} +
+ + {/* 2. satır: dosya adı ve üretilen kod */} +
+ {f.fileName} + + + {isCustom ? f.customComponentName : f.listFormCode} + +
+ + {/* 3. satır: yol ayrımı (Custom / List), layout ve menü adresi */} +
+ + {isCustom ? : } + {translate( + isCustom + ? '::App.WizardStepComponent.CustomComponent' + : '::App.WizardStepComponent.ListComponent', + )} + + + {/* Custom yolunda layout kavramı yoktur. */} + {!isCustom && f.defaultLayout && ( + + {f.defaultLayout} + + )} + + {f.menuUrl && ( + + + {f.menuUrl} + + )} +
+
+
+ + {renderActions(f, 'flex items-center gap-1 shrink-0 sm:ml-3')} +
+ ) + })} +
+ )}
{importFile && ( diff --git a/ui/src/views/developerKit/ComponentManager.tsx b/ui/src/views/developerKit/ComponentManager.tsx index 12130f6f..683aecc8 100644 --- a/ui/src/views/developerKit/ComponentManager.tsx +++ b/ui/src/views/developerKit/ComponentManager.tsx @@ -12,6 +12,8 @@ import { FaExternalLinkAlt, FaCog, FaSyncAlt, + FaTh, + FaList, } from 'react-icons/fa' import Widget from '@/components/common/Widget' import { ROUTES_ENUM } from '@/routes/route.constant' @@ -26,6 +28,12 @@ import { usePermission } from '@/utils/hooks/usePermission' import { COMPONENT_PERMISSION } from '@/constants/permission.constant' import Input from '@/components/ui/Input' import Select from '@/components/ui/Select' +import type { CustomComponent } from '@/proxy/developerKit/models' +import { useStoreActions, useStoreState } from '@/store/store' +import type { ListViewLayoutType } from '../admin/listForm/edit/types' + +/** Layout tercihi diger listelerle ayni yerde (admin.lists.states) saklanir. */ +const VIEW_STATE_CODE = 'developerkit-components' const ComponentManager: React.FC = () => { const { @@ -40,6 +48,12 @@ const ComponentManager: React.FC = () => { const [filterActive, setFilterActive] = useState<'all' | 'active' | 'inactive'>('all') const [isRefreshing, setIsRefreshing] = useState(false) const [editorComponentId, setEditorComponentId] = useState(undefined) + const { states } = useStoreState((state) => state.admin.lists) + const { setStates } = useStoreActions((a) => a.admin.lists) + const viewMode: ListViewLayoutType = + states.find((s) => s.listFormCode === VIEW_STATE_CODE)?.layout === 'grid' ? 'grid' : 'card' + const setViewMode = (layout: ListViewLayoutType) => + setStates({ listFormCode: VIEW_STATE_CODE, layout }) // Calculate statistics const totalComponents = components?.length || 0 @@ -104,6 +118,85 @@ const ComponentManager: React.FC = () => { } } + const renderStatusToggle = (component: CustomComponent) => ( + + ) + + const renderActions = (component: CustomComponent) => ( +
+ {canUpdate && ( + + )} + + + {canDelete && ( + + )} +
+ ) + return (
@@ -181,6 +274,22 @@ const ComponentManager: React.FC = () => {
)} +
+
{/* Components List */} @@ -191,148 +300,158 @@ const ComponentManager: React.FC = () => { ) : filteredComponents?.length > 0 ? ( -
- {filteredComponents.map((component) => ( -
-
-
- {/* Sol taraf */} -
-
-

- {component.name} -

-
-
+ viewMode === 'card' ? ( +
+ {filteredComponents.map((component) => ( +
+
+
+ {/* Sol taraf */} +
+
+

+ {component.name} +

+
+
-

- {parseComponentDependencies(component.dependencies).join(', ') || - translate('::App.DeveloperKitComponent.NoDependencies')} -

- -

- {component.routePath} -

- - {component.description && ( -

- {component.description} +

+ {parseComponentDependencies(component.dependencies).join(', ') || + translate('::App.DeveloperKitComponent.NoDependencies')}

- )} - {componentErrors[component.name] && (

- {componentErrors[component.name]} + {component.routePath}

- )} -
- {/* Sağ taraf */} - {component.lastModificationTime && ( -
- - - {new Date(component.lastModificationTime).toLocaleDateString() ?? ''} - -
- )} -
- - {/* Actions */} -
-
- + + {componentErrors[component.name] && ( +

+ {componentErrors[component.name]} +

+ )} +
+ + {/* Sag taraf */} + {component.lastModificationTime && ( +
+ + + {new Date(component.lastModificationTime).toLocaleDateString() ?? ''} + +
+ )}
-
- {canUpdate && ( - - )} - - - {canDelete && ( - - )} + + {/* Actions */} +
+ {renderStatusToggle(component)} + {renderActions(component)}
-
- ))} -
+ ))} +
+ ) : ( +
+ + + + + + + + + + + + + + {filteredComponents.map((component) => ( + + + + + + + + + + ))} + +
+ {translate('::App.Platform.Name')} + + {translate('::App.Platform.Dependencies')} + + {translate('::App.Platform.RoutePath')} + + {translate('::App.Platform.Description')} + + {translate('::App.Platform.Modified')} + + {translate('::App.Platform.Status')} + + {translate('::App.Platform.Actions')} +
+
+
+ + {component.name} + +
+ {componentErrors[component.name] && ( +

+ {componentErrors[component.name]} +

+ )} +
+ {parseComponentDependencies(component.dependencies).join(', ') || + translate('::App.DeveloperKitComponent.NoDependencies')} + + {component.routePath} + + {component.description} + + {component.lastModificationTime + ? new Date(component.lastModificationTime).toLocaleDateString() + : ''} + {renderStatusToggle(component)} +
{renderActions(component)}
+
+
+ ) ) : (
diff --git a/ui/src/views/forum/admin/CategoryManagement.tsx b/ui/src/views/forum/admin/CategoryManagement.tsx index 128edc90..cb86eaef 100644 --- a/ui/src/views/forum/admin/CategoryManagement.tsx +++ b/ui/src/views/forum/admin/CategoryManagement.tsx @@ -16,7 +16,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization' import { Formik, Form, Field, FieldProps } from 'formik' import * as Yup from 'yup' import { FormContainer, FormItem } from '@/components/ui/Form' -import { Input, Checkbox, Button, Select } from '@/components/ui' +import { Input, Checkbox, Button, Select, Dialog } from '@/components/ui' import { ConfirmDialog } from '@/components/shared' import { CategoryIconOption, resolveCategoryIconOptions } from './categoryIcons' @@ -69,6 +69,11 @@ export function CategoryManagement({ setShowCreateForm(true) } + const handleCloseForm = () => { + setShowCreateForm(false) + setEditingCategory(null) + } + const handleToggleActive = async (category: ForumCategory) => { try { await onUpdateCategoryActiveState(category.id) @@ -87,16 +92,24 @@ export function CategoryManagement({ return (
- {/* Create/Edit Form */} - {showCreateForm && ( -
-

+ {/* Create/Edit Modal */} + +
+

{editingCategory ? translate('::App.ForumCategoryManagement.EditCategory') : translate('::App.ForumCategoryManagement.AddCategory')}

+
+
-
- )} +
{/* Categories List */}
@@ -321,7 +327,7 @@ export function CategoryManagement({
diff --git a/ui/src/views/forum/admin/PostManagement.tsx b/ui/src/views/forum/admin/PostManagement.tsx index 6907e80d..0eca6528 100644 --- a/ui/src/views/forum/admin/PostManagement.tsx +++ b/ui/src/views/forum/admin/PostManagement.tsx @@ -15,7 +15,7 @@ import { useStoreState } from '@/store/store' import { useLocalization } from '@/utils/hooks/useLocalization' import { Formik, Form, Field, FieldProps } from 'formik' import * as Yup from 'yup' -import { FormContainer, FormItem, Button } from '@/components/ui' +import { FormContainer, FormItem, Button, Dialog } from '@/components/ui' import { ConfirmDialog } from '@/components/shared' import { formatForumDate, sanitizeForumHtml, stripHtml } from '../forum/utils' import { @@ -72,6 +72,11 @@ export function PostManagement({ setShowCreateForm(true) } + const handleCloseForm = () => { + setShowCreateForm(false) + setEditingPost(null) + } + const handleToggleAcceptedAnswer = async (post: ForumPost) => { try { if (post.isAcceptedAnswer) { @@ -89,15 +94,22 @@ export function PostManagement({ return (
- {/* Create/Edit Form */} - {showCreateForm && ( -
-

+ {/* Create/Edit Modal */} + +
+

{editingPost ? translate('::App.ForumPostManagement.EditPost') : translate('::App.ForumPostManagement.AddPost')}

+
+
-
- )} +
{/* Posts List */}
@@ -304,7 +309,7 @@ export function PostManagement({

{post.authorName}

{post.isAcceptedAnswer && (
- + {translate('::App.ForumPostManagement.AcceptedAnswer')}
)} @@ -336,7 +341,7 @@ export function PostManagement({
diff --git a/ui/src/views/forum/admin/TopicManagement.tsx b/ui/src/views/forum/admin/TopicManagement.tsx index 9a0aa32f..abe9d4af 100644 --- a/ui/src/views/forum/admin/TopicManagement.tsx +++ b/ui/src/views/forum/admin/TopicManagement.tsx @@ -17,7 +17,7 @@ import { useStoreState } from '@/store/store' import { useLocalization } from '@/utils/hooks/useLocalization' import { Formik, Form, Field, FieldProps } from 'formik' import * as Yup from 'yup' -import { FormContainer, FormItem, Button, Select } from '@/components/ui' +import { FormContainer, FormItem, Button, Select, Dialog } from '@/components/ui' import { ConfirmDialog } from '@/components/shared' import { formatForumDate } from '../forum/utils' @@ -84,6 +84,11 @@ export function TopicManagement({ setShowCreateForm(true) } + const handleCloseForm = () => { + setShowCreateForm(false) + setEditingTopic(null) + } + const handlePin = async (topic: ForumTopic) => { try { if (topic.isPinned) { @@ -141,15 +146,22 @@ export function TopicManagement({ return (
- {/* Create/Edit Form */} - {showCreateForm && ( -
-

+ {/* Create/Edit Modal */} + +
+

{editingTopic ? translate('::App.ForumTopicManagement.EditTopic') : translate('::App.ForumTopicManagement.AddTopic')}

+
+
-
- )} +
{/* Topics List */}
@@ -366,7 +371,7 @@ export function TopicManagement({