Dialog Maximize özelliği eklendi

This commit is contained in:
Sedat ÖZTÜRK 2026-05-13 14:31:15 +03:00
parent df9b6ff362
commit 66037bf001
16 changed files with 1336 additions and 1029 deletions

View file

@ -648,6 +648,12 @@
"en": "Forum", "en": "Forum",
"tr": "Forum" "tr": "Forum"
}, },
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard",
"en": "Dashboard",
"tr": "Gösterge Paneli"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Forum.Dashboard.Categories", "key": "App.Forum.Dashboard.Categories",

View file

@ -52,9 +52,11 @@
.dialog-content.maximized { .dialog-content.maximized {
border-radius: 0 !important; border-radius: 0 !important;
max-height: 100vh !important;
height: 100vh !important; height: 100vh !important;
margin: 0 !important; margin: 0 !important;
overflow: hidden;
display: flex;
flex-direction: column;
@apply my-0; @apply my-0;
} }

View file

@ -1,4 +1,5 @@
import React, { useState, useEffect, useMemo } from 'react' import React, { useState, useEffect, useMemo } from 'react'
import classNames from 'classnames'
import { import {
FaUpload, FaUpload,
FaCheckCircle, FaCheckCircle,
@ -18,6 +19,7 @@ import { ListFormImportDto, ListFormImportExecuteDto } from '@/proxy/imports/mod
import { ImportService } from '@/services/import.service' import { ImportService } from '@/services/import.service'
import { GridDto } from '@/proxy/form/models' import { GridDto } from '@/proxy/form/models'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { useDialogContext } from '@/components/ui/Dialog/Dialog'
interface ImportDashboardProps { interface ImportDashboardProps {
gridDto: GridDto gridDto: GridDto
@ -214,16 +216,17 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
} }
const editableColumns = getEditableColumns() const editableColumns = getEditableColumns()
const { isMaximized } = useDialogContext()
return ( return (
<div className="mx-auto px-4 py-2"> <div className="flex flex-col w-full mt-4">
{/* Navigation Tabs */} {/* Navigation Tabs */}
<div className="flex space-x-1 mb-4 bg-white rounded-lg p-1 shadow-sm border border-slate-200"> <div className="flex space-x-1 mb-4 bg-white rounded-lg p-1 shadow-sm border border-slate-200 flex-shrink-0">
{['import', 'preview', 'history'].map((tab) => ( {['import', 'preview', 'history'].map((tab) => (
<button <button
key={tab} key={tab}
onClick={() => setActiveTab(tab as TabNames)} onClick={() => setActiveTab(tab as TabNames)}
className={`px-6 py-3 rounded-md font-medium transition-all duration-200 flex items-center space-x-2 ${ className={`px-3 py-2 rounded-md font-medium transition-all duration-200 flex items-center space-x-2 ${
activeTab === tab activeTab === tab
? 'bg-blue-500 text-white shadow-md' ? 'bg-blue-500 text-white shadow-md'
: 'text-slate-600 hover:text-slate-800 hover:bg-slate-50' : 'text-slate-600 hover:text-slate-800 hover:bg-slate-50'
@ -238,6 +241,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
</div> </div>
{/* Content */} {/* Content */}
<div className={classNames(isMaximized ? 'flex-1 min-h-0 overflow-auto' : '')}>
{activeTab === 'import' && ( {activeTab === 'import' && (
<div className="space-y-6"> <div className="space-y-6">
{/* Template Generator & File Upload - Side by Side */} {/* Template Generator & File Upload - Side by Side */}
@ -300,7 +304,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
{editableColumns.map((column: any) => ( {editableColumns.map((column: any) => (
<tr key={column.fieldName} className="hover:bg-slate-50"> <tr key={column.fieldName} className="hover:bg-slate-50">
<td className="px-2 py-2 font-medium text-slate-800"> <td className="px-2 py-2 font-medium text-slate-800">
{column.captionName || column.fieldName} {translate('::' + column.captionName)}
</td> </td>
<td className="px-4 py-2 text-slate-600"> <td className="px-4 py-2 text-slate-600">
<span <span
@ -535,7 +539,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
{translate('::App.Listforms.ImportManager.LoadingExecutionDetails')} {translate('::App.Listforms.ImportManager.LoadingExecutionDetails')}
</span> </span>
</div> </div>
) : sessionExecutes[session.id] && sessionExecutes[session.id].length > 0 ? ( ) : sessionExecutes[session.id] &&
sessionExecutes[session.id].length > 0 ? (
<div className="space-y-2"> <div className="space-y-2">
{sessionExecutes[session.id].map((execute) => ( {sessionExecutes[session.id].map((execute) => (
<div key={execute.id} className="p-3 rounded-lg"> <div key={execute.id} className="p-3 rounded-lg">
@ -633,5 +638,6 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
</div> </div>
)} )}
</div> </div>
</div>
) )
} }

View file

@ -5,10 +5,54 @@ import WindowControls from '../WindowControls'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { SCREENS } from '@/utils/tailwind' import { SCREENS } from '@/utils/tailwind'
import useWindowSize from '../hooks/useWindowSize' import useWindowSize from '../hooks/useWindowSize'
import { useState, useCallback } from 'react' import { useState, useCallback, createContext, useContext } from 'react'
import type ReactModal from 'react-modal' import type ReactModal from 'react-modal'
import type { MouseEvent } from 'react' import type { MouseEvent, ReactNode } from 'react'
import { Container } from '@/components/shared'
interface DialogContextValue {
isMaximized: boolean
}
const DialogContext = createContext<DialogContextValue>({ isMaximized: false })
export const useDialogContext = () => useContext(DialogContext)
const DialogBody = ({
children,
className,
}: {
children?: ReactNode
className?: string
}) => {
const { isMaximized } = useContext(DialogContext)
return (
<div
className={classNames(
isMaximized && 'flex-1 min-h-0 flex flex-col overflow-hidden',
className,
)}
>
{children}
</div>
)
}
DialogBody.displayName = 'Dialog.Body'
const DialogFooter = ({
children,
className,
}: {
children?: ReactNode
className?: string
}) => {
const { isMaximized } = useContext(DialogContext)
return (
<div className={classNames(isMaximized && 'flex-shrink-0', className)}>
{children}
</div>
)
}
DialogFooter.displayName = 'Dialog.Footer'
export interface DialogProps extends ReactModal.Props { export interface DialogProps extends ReactModal.Props {
closable?: boolean closable?: boolean
@ -186,7 +230,15 @@ const Dialog = (props: DialogProps) => {
> >
{closable && !showWindowControls && renderCloseButton} {closable && !showWindowControls && renderCloseButton}
{closable && showWindowControls && renderWindowControls} {closable && showWindowControls && renderWindowControls}
<DialogContext.Provider value={{ isMaximized }}>
{isMaximized ? (
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
{children} {children}
</div>
) : (
children
)}
</DialogContext.Provider>
</motion.div> </motion.div>
</Modal> </Modal>
) )
@ -194,4 +246,13 @@ const Dialog = (props: DialogProps) => {
Dialog.displayName = 'Dialog' Dialog.displayName = 'Dialog'
export default Dialog type DialogType = typeof Dialog & {
Body: typeof DialogBody
Footer: typeof DialogFooter
}
const DialogWithSubComponents = Dialog as DialogType
DialogWithSubComponents.Body = DialogBody
DialogWithSubComponents.Footer = DialogFooter
export default DialogWithSubComponents

View file

@ -1,7 +1,9 @@
import classNames from 'classnames'
import { FaChevronRight, FaChevronDown } from 'react-icons/fa' import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
import Container from '@/components/shared/Container' import Container from '@/components/shared/Container'
import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui' import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
import { useConfig } from '@/components/ui/ConfigProvider' import { useConfig } from '@/components/ui/ConfigProvider'
import { useDialogContext } from '@/components/ui/Dialog/Dialog'
import Notification from '@/components/ui/Notification' import Notification from '@/components/ui/Notification'
import { import {
GetPermissionListResultDto, GetPermissionListResultDto,
@ -17,6 +19,166 @@ import { ChangeEvent, useEffect, useMemo, useState } from 'react'
type OpenState = Record<string, boolean> type OpenState = Record<string, boolean>
function PermissionDialogContent({
name,
translate,
permissionList,
selectedGroup,
selectedGroupPermissions,
filteredPermissions,
isAllSelected,
isAllSelectedForGroup,
isLoading,
searchTerm,
openPermissions,
mode,
onSelectAll,
onDialogClose,
onDialogOk,
handleExpandAll,
handleCollapseAll,
changeGroup,
togglePermission,
isParent,
getChildren,
onClickCheckbox,
setSearchTerm,
setCopyDialogOpen,
}: any) {
const { isMaximized } = useDialogContext()
return (
<>
<h5 className="mb-1 flex-shrink-0">
{translate('::Permission')} - {name}
</h5>
<hr className="mt-1 mb-2 flex-shrink-0"></hr>
<div className="flex flex-col md:flex-row gap-4 mb-1 flex-shrink-0">
<div className="w-full md:w-1/3">
<Checkbox name="all" checked={isAllSelected} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInAllTabs')}
</Checkbox>
</div>
<div className="w-full md:w-2/3">
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInThisTab')}
</Checkbox>
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}>
{translate('::ListForms.ListFormEdit.ExpandAll')}
</Button>
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}>
{translate('::ListForms.ListFormEdit.CollapseAll')}
</Button>
</div>
</div>
<div
className={classNames(
'flex flex-col md:flex-row gap-4',
isMaximized ? 'flex-1 min-h-0' : '',
)}
>
<div
className={classNames(
'w-full md:w-1/3 overflow-y-auto',
isMaximized ? 'min-h-0' : 'max-h-[450px]',
)}
>
<hr className="mb-2"></hr>
<Menu
className="w-full"
variant={mode}
defaultActiveKeys={[selectedGroup?.displayName ?? '']}
>
{permissionList?.groups.map((group: any) => (
<Menu.MenuItem
key={group.name}
className="break-all whitespace-normal"
eventKey={group.name}
onSelect={changeGroup}
>
{translate('::' + group.displayName)} (
{group.permissions.filter((a: any) => a.isGranted).length})
</Menu.MenuItem>
))}
</Menu>
</div>
<div
className={classNames(
'w-full md:w-2/3 overflow-y-auto flex flex-col',
isMaximized ? 'min-h-0' : 'max-h-[450px]',
)}
>
<hr className="mb-2 flex-shrink-0"></hr>
<div className="flex items-center gap-2 mb-2 flex-shrink-0">
<Input
size="sm"
placeholder={translate('::Search')}
value={searchTerm}
onChange={(e: any) => setSearchTerm(e.target.value)}
/>
</div>
<div className="my-1">
{filteredPermissions.map((permission: any) => {
const isParentPerm = isParent(permission)
const permKey = permission.name || ''
return (
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
{isParentPerm ? (
<button
onClick={(e) => {
e.stopPropagation()
togglePermission(permKey)
}}
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
>
{openPermissions[permKey] || searchTerm ? (
<FaChevronDown className="text-gray-500 text-xs" />
) : (
<FaChevronRight className="text-gray-500 text-xs" />
)}
</button>
) : (
<div className="w-5" />
)}
<Checkbox
name={permission.name}
checked={permission.isGranted}
onChange={() => onClickCheckbox(permission)}
>
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
{translate('::' + permission.displayName)}
</span>
</Checkbox>
</div>
</div>
)
})}
</div>
</div>
</div>
<Dialog.Footer className="flex justify-between items-center mt-4 pt-3 border-t">
<div className="flex items-center">
<Button variant="solid" color="sky-500" onClick={() => setCopyDialogOpen(true)}>
{translate('::AbpIdentity.Roles.CopyPermissions')}
</Button>
</div>
<div className="text-right">
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={onDialogClose}>
{translate('::Cancel')}
</Button>
<Button variant="solid" loading={isLoading} onClick={onDialogOk}>
{isLoading ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
</Dialog.Footer>
</>
)
}
function RolesPermission({ function RolesPermission({
open, open,
onDialogClose, onDialogClose,
@ -368,122 +530,34 @@ function RolesPermission({
onClose={onDialogClose} onClose={onDialogClose}
onRequestClose={onDialogClose} onRequestClose={onDialogClose}
> >
<h5 className="mb-1"> <Dialog.Body className="flex flex-col">
{translate('::Permission')} - {name} <PermissionDialogContent
</h5> name={name}
<hr className="mt-1 mb-2"></hr> translate={translate}
permissionList={permissionList}
<div className="flex flex-col md:flex-row gap-4 mb-1"> selectedGroup={selectedGroup}
<div className="w-full md:w-1/3"> selectedGroupPermissions={selectedGroupPermissions}
<Checkbox name="all" checked={isAllSelected} onChange={onSelectAll}> filteredPermissions={filteredPermissions}
{translate('AbpPermissionManagement::SelectAllInAllTabs')} isAllSelected={isAllSelected}
</Checkbox> isAllSelectedForGroup={isAllSelectedForGroup}
</div> isLoading={isLoading}
<div className="w-full md:w-2/3"> searchTerm={searchTerm}
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}> openPermissions={openPermissions}
{translate('AbpPermissionManagement::SelectAllInThisTab')} mode={mode}
</Checkbox> onSelectAll={onSelectAll}
onDialogClose={onDialogClose}
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}> onDialogOk={onDialogOk}
{translate('::ListForms.ListFormEdit.ExpandAll')} handleExpandAll={handleExpandAll}
</Button> handleCollapseAll={handleCollapseAll}
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}> changeGroup={changeGroup}
{translate('::ListForms.ListFormEdit.CollapseAll')} togglePermission={togglePermission}
</Button> isParent={isParent}
</div> getChildren={getChildren}
</div> onClickCheckbox={onClickCheckbox}
setSearchTerm={setSearchTerm}
<div className="flex flex-col md:flex-row gap-4"> setCopyDialogOpen={setCopyDialogOpen}
<div className="w-full md:w-1/3 max-h-[450px] overflow-y-auto">
<hr className="mb-2"></hr>
<Menu
className="w-full"
variant={mode}
defaultActiveKeys={[selectedGroup?.displayName ?? '']}
>
{permissionList?.groups.map((group: any) => (
<Menu.MenuItem
key={group.name}
className="break-all whitespace-normal"
eventKey={group.name}
onSelect={changeGroup}
>
{translate('::' + group.displayName)} (
{group.permissions.filter((a: any) => a.isGranted).length})
</Menu.MenuItem>
))}
</Menu>
</div>
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
<hr className="mb-2"></hr>
<div className="flex items-center gap-2 mb-2">
<Input
size="sm"
className=""
placeholder={translate('::Search')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/> />
</div> </Dialog.Body>
<div className="my-1">
{filteredPermissions.map((permission) => {
const isParentPerm = isParent(permission)
const permKey = permission.name || ''
const children = getChildren(permKey)
return (
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
{/* Expand Icon */}
{isParentPerm ? (
<button
onClick={(e) => {
e.stopPropagation()
togglePermission(permKey)
}}
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
>
{openPermissions[permKey] || searchTerm ? (
<FaChevronDown className="text-gray-500 text-xs" />
) : (
<FaChevronRight className="text-gray-500 text-xs" />
)}
</button>
) : (
<div className="w-5" />
)}
{/* Checkbox */}
<Checkbox
name={permission.name}
checked={permission.isGranted}
onChange={() => onClickCheckbox(permission)}
>
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
{translate('::' + permission.displayName)}
</span>
</Checkbox>
</div>
</div>
)
})}
</div>
</div>
</div>
<div className="flex justify-between items-center mt-6">
<div className="flex items-center">
<Button variant="solid" color="sky-500" onClick={() => setCopyDialogOpen(true)}>
{translate('::AbpIdentity.Roles.CopyPermissions')}
</Button>
</div>
<div className="text-right">
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={onDialogClose}>
{translate('::Cancel')}
</Button>
<Button variant="solid" loading={isLoading} onClick={onDialogOk}>
{isLoading ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
</div>
</Dialog> </Dialog>
{/* Copy Permissions Dialog */} {/* Copy Permissions Dialog */}

View file

@ -1,7 +1,9 @@
import classNames from 'classnames'
import { FaChevronRight, FaChevronDown } from 'react-icons/fa' import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
import { Badge, Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui' import { Badge, Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
type OpenState = Record<string, boolean> type OpenState = Record<string, boolean>
import { useConfig } from '@/components/ui/ConfigProvider' import { useConfig } from '@/components/ui/ConfigProvider'
import { useDialogContext } from '@/components/ui/Dialog/Dialog'
import Notification from '@/components/ui/Notification' import Notification from '@/components/ui/Notification'
import { import {
GetPermissionListResultDto, GetPermissionListResultDto,
@ -16,6 +18,166 @@ import { useStoreActions, useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { ChangeEvent, useEffect, useMemo, useState } from 'react' import { ChangeEvent, useEffect, useMemo, useState } from 'react'
function UserPermissionDialogContent({
name,
translate,
permissionList,
selectedGroup,
selectedGroupPermissions,
filteredPermissions,
isAllSelected,
isAllSelectedForGroup,
isLoading,
searchTerm,
openPermissions,
mode,
providerName,
onSelectAll,
onDialogClose,
onDialogOk,
handleExpandAll,
handleCollapseAll,
changeGroup,
togglePermission,
isParent,
onClickCheckbox,
setSearchTerm,
}: any) {
const { isMaximized } = useDialogContext()
return (
<>
<h5 className="mb-1 flex-shrink-0">
{translate('::Permission')} - {name}
</h5>
<hr className="mt-1 mb-2 flex-shrink-0"></hr>
<div className="flex flex-col md:flex-row gap-4 mb-1 flex-shrink-0">
<div className="w-full md:w-1/3">
<Checkbox name="all" checked={isAllSelected} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInAllTabs')}
</Checkbox>
</div>
<div className="w-full md:w-2/3">
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInThisTab')}
</Checkbox>
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}>
{translate('::ListForms.ListFormEdit.ExpandAll')}
</Button>
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}>
{translate('::ListForms.ListFormEdit.CollapseAll')}
</Button>
</div>
</div>
<div
className={classNames(
'flex flex-col md:flex-row gap-4',
isMaximized ? 'flex-1 min-h-0' : '',
)}
>
<div
className={classNames(
'w-full md:w-1/3 overflow-y-auto',
isMaximized ? 'min-h-0' : 'max-h-[450px]',
)}
>
<hr className="mb-2"></hr>
<Menu variant={mode} defaultActiveKeys={[selectedGroup?.displayName ?? '']}>
{permissionList?.groups.map((group: any) => (
<Menu.MenuItem
key={group.name}
className="break-all whitespace-normal"
eventKey={group.name}
onSelect={() => changeGroup(group.name)}
>
{translate('::' + group.displayName)} (
{group.permissions.filter((a: any) => a.isGranted).length})
</Menu.MenuItem>
))}
</Menu>
</div>
<div
className={classNames(
'w-full md:w-2/3 overflow-y-auto flex flex-col',
isMaximized ? 'min-h-0' : 'max-h-[450px]',
)}
>
<hr className="mb-2 flex-shrink-0"></hr>
<div className="flex items-center gap-2 mb-2 flex-shrink-0">
<Input
size="sm"
placeholder={translate('::Search')}
value={searchTerm}
onChange={(e: any) => setSearchTerm(e.target.value)}
/>
</div>
<div className="my-1">
{filteredPermissions.map((permission: any) => {
const isParentPerm = isParent(permission)
const permKey = permission.name || ''
return (
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
{isParentPerm ? (
<button
onClick={(e) => {
e.stopPropagation()
togglePermission(permKey)
}}
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
>
{openPermissions[permKey] || searchTerm ? (
<FaChevronDown className="text-gray-500 text-xs" />
) : (
<FaChevronRight className="text-gray-500 text-xs" />
)}
</button>
) : (
<div className="w-5" />
)}
<Checkbox
name={permission.name}
checked={permission.isGranted}
onChange={() => onClickCheckbox(permission)}
disabled={permission.grantedProviders.some(
(provider: any) => provider.providerName === 'R',
)}
>
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
{translate('::' + permission.displayName)}
{permission.grantedProviders.map((provider: any) => {
const badgeContent =
provider.providerName !== providerName
? `${provider.providerName}: ${provider.providerKey}`
: provider.providerName
return (
<Badge key={provider.providerKey} className="m-2" content={badgeContent} />
)
})}
</span>
</Checkbox>
</div>
</div>
)
})}
</div>
</div>
</div>
<Dialog.Footer className="flex justify-end items-center mt-4 pt-3 border-t gap-2">
<Button variant="plain" onClick={onDialogClose}>
Cancel
</Button>
<Button variant="solid" loading={isLoading} onClick={onDialogOk}>
{isLoading ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</Dialog.Footer>
</>
)
}
function UsersPermission({ function UsersPermission({
open, open,
onDialogClose, onDialogClose,
@ -297,125 +459,33 @@ function UsersPermission({
onClose={onDialogClose} onClose={onDialogClose}
onRequestClose={onDialogClose} onRequestClose={onDialogClose}
> >
<h5 className="mb-1"> <Dialog.Body className="flex flex-col">
{translate('::Permission')} - {name} <UserPermissionDialogContent
</h5> name={name}
<hr className="mt-1 mb-2"></hr> translate={translate}
permissionList={permissionList}
<div className="flex flex-col md:flex-row gap-4"> selectedGroup={selectedGroup}
<div className="w-full md:w-1/3"> selectedGroupPermissions={selectedGroupPermissions}
<Checkbox name="all" checked={isAllSelected} onChange={onSelectAll}> filteredPermissions={filteredPermissions}
{translate('AbpPermissionManagement::SelectAllInAllTabs')} isAllSelected={isAllSelected}
</Checkbox> isAllSelectedForGroup={isAllSelectedForGroup}
</div> isLoading={isLoading}
<div className="w-full md:w-2/3"> searchTerm={searchTerm}
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}> openPermissions={openPermissions}
{translate('AbpPermissionManagement::SelectAllInThisTab')} mode={mode}
</Checkbox> providerName={providerName}
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}> onSelectAll={onSelectAll}
{translate('::ListForms.ListFormEdit.ExpandAll')} onDialogClose={onDialogClose}
</Button> onDialogOk={onDialogOk}
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}> handleExpandAll={handleExpandAll}
{translate('::ListForms.ListFormEdit.CollapseAll')} handleCollapseAll={handleCollapseAll}
</Button> changeGroup={changeGroup}
</div> togglePermission={togglePermission}
</div> isParent={isParent}
onClickCheckbox={onClickCheckbox}
<div className="flex flex-col md:flex-row gap-4"> setSearchTerm={setSearchTerm}
<div className="w-full md:w-1/3 max-h-[450px] overflow-y-auto">
<hr className="mb-2"></hr>
<Menu variant={mode} defaultActiveKeys={[selectedGroup?.displayName ?? '']}>
{permissionList?.groups.map((group) => (
<Menu.MenuItem
key={group.name}
className="break-all whitespace-normal"
eventKey={group.name}
onSelect={() => {
changeGroup(group.name)
}}
>
{translate('::' + group.displayName)} (
{group.permissions.filter((a) => a.isGranted).length})
</Menu.MenuItem>
))}
</Menu>
</div>
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
<hr className="mb-2"></hr>
<div className="flex items-center gap-2 mb-2">
<Input
size="sm"
className=""
placeholder={translate('::Search')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/> />
</div> </Dialog.Body>
<div className="my-1">
{filteredPermissions.map((permission) => {
const isParentPerm = isParent(permission)
const permKey = permission.name || ''
const children = getChildren(permKey)
return (
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
{/* Expand Icon */}
{isParentPerm ? (
<button
onClick={(e) => {
e.stopPropagation()
togglePermission(permKey)
}}
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
>
{openPermissions[permKey] || searchTerm ? (
<FaChevronDown className="text-gray-500 text-xs" />
) : (
<FaChevronRight className="text-gray-500 text-xs" />
)}
</button>
) : (
<div className="w-5" />
)}
{/* Checkbox */}
<Checkbox
name={permission.name}
checked={permission.isGranted}
onChange={() => onClickCheckbox(permission)}
disabled={permission.grantedProviders.some((provider) => provider.providerName === 'R')}
>
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
{translate('::' + permission.displayName)}
{permission.grantedProviders.map((provider) => {
const badgeContent = provider.providerName !== providerName
? `${provider.providerName}: ${provider.providerKey}`
: provider.providerName;
return (
<Badge
key={provider.providerKey}
className="m-2"
content={badgeContent}
/>
);
})}
</span>
</Checkbox>
</div>
</div>
)
})}
</div>
</div>
</div>
<div className="text-right mt-6">
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={onDialogClose}>
Cancel
</Button>
<Button variant="solid" loading={isLoading} onClick={onDialogOk}>
{isLoading ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
</Dialog> </Dialog>
) : ( ) : (
<></> <></>

View file

@ -1,21 +1,20 @@
import classNames from 'classnames'
import { useState } from 'react' import { useState } from 'react'
import { Badge, Button } from '@/components/ui' import { Badge, Button } from '@/components/ui'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Dialog, Notification, toast } from '@/components/ui' import { Dialog, Notification, toast } from '@/components/ui'
import { useDialogContext } from '@/components/ui/Dialog/Dialog'
import type { BranchSeedResultDto } from '@/proxy/branch/seed' import type { BranchSeedResultDto } from '@/proxy/branch/seed'
import { runBranchSeed } from '@/services/branch.service' import { runBranchSeed } from '@/services/branch.service'
function BranchSeed({ function BranchSeedContent({
open,
onDialogClose,
id, id,
name, name,
}: { }: {
open: boolean
onDialogClose: () => void
id: string id: string
name: string name: string
}) { }) {
const { isMaximized } = useDialogContext()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [result, setResult] = useState<BranchSeedResultDto | null>(null) const [result, setResult] = useState<BranchSeedResultDto | null>(null)
@ -52,48 +51,45 @@ function BranchSeed({
} }
return ( return (
<Container> <>
<Dialog <h5 className="mb-4 text-lg font-semibold flex-shrink-0">Branch Seed - {name}</h5>
width="75%" <hr className="mb-3 flex-shrink-0" />
height="60%"
isOpen={open}
onClose={onDialogClose}
onRequestClose={onDialogClose}
>
<h5 className="mb-4 text-lg font-semibold">Branch Seed - {name}</h5>
<hr className="mb-3" />
<div className="mt-3 space-y-4"> <div
{/* Başlık ve buton aynı satırda */} className={classNames(
<div className="flex items-center justify-between"> 'mt-3 space-y-4',
<div> isMaximized ? 'flex-1 min-h-0 flex flex-col overflow-hidden' : '',
{result && )}
(result.success ? ( >
{/* Sonuç durumu */}
{result && (
<div className="flex-shrink-0">
{result.success ? (
<span className="text-green-600 font-semibold"> Seed İşlemi Başarılı</span> <span className="text-green-600 font-semibold"> Seed İşlemi Başarılı</span>
) : ( ) : (
<span className="text-red-600 font-semibold"> Seed İşlemi Başarısız</span> <span className="text-red-600 font-semibold"> Seed İşlemi Başarısız</span>
))} )}
</div>
<div>
<Button size="sm" variant="solid" onClick={handleRunSeed} loading={isLoading}>
{isLoading ? 'Seed Çalıştırılıyor...' : 'Seed İşlemini Başlat'}
</Button>
</div>
</div> </div>
)}
{/* Sonuç Detayları */} {/* Sonuç Detayları */}
{result && ( {result && (
<> <>
<p className="font-medium text-gray-700">{result.message}</p> <p className="font-medium text-gray-700 flex-shrink-0">{result.message}</p>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600 flex-shrink-0">
Toplam eklenen kayıt:{' '} Toplam eklenen kayıt:{' '}
<span className="font-bold text-blue-600">{result.totalInsertedCount}</span> <span className="font-bold text-blue-600">{result.totalInsertedCount}</span>
</p> </p>
{/* Detay Tablosu */} {/* Detay Tablosu */}
{result.details.length > 0 && ( {result.details.length > 0 && (
<div className="overflow-x-auto overflow-y-auto border border-gray-200 rounded-md max-h-[50vh]"> <div
className={classNames(
'overflow-x-auto overflow-y-auto border border-gray-200 rounded-md',
isMaximized ? 'flex-1 min-h-0' : 'max-h-[50vh]',
)}
>
<table className="min-w-full text-sm"> <table className="min-w-full text-sm">
<thead className="bg-gray-100 sticky top-0"> <thead className="bg-gray-100 sticky top-0">
<tr> <tr>
@ -112,13 +108,10 @@ function BranchSeed({
<Badge content={d.insertedCount} /> <Badge content={d.insertedCount} />
</div> </div>
</td> </td>
{/* Eklenen kolonunu tek satır formatında göster */}
<td className="border px-3 py-2">{d.insertedItems.join(', ')}</td> <td className="border px-3 py-2">{d.insertedItems.join(', ')}</td>
<td className="border px-3 py-2 text-yellow-700"> <td className="border px-3 py-2 text-yellow-700">
{d.warnings.join(', ')} {d.warnings.join(', ')}
</td> </td>
<td className="border px-3 py-2 text-red-700">{d.errors.join(', ')}</td> <td className="border px-3 py-2 text-red-700">{d.errors.join(', ')}</td>
</tr> </tr>
))} ))}
@ -128,11 +121,44 @@ function BranchSeed({
)} )}
{!result.details.length && ( {!result.details.length && (
<p className="text-gray-500 text-sm">Hiç detay bilgisi bulunamadı.</p> <p className="text-gray-500 text-sm flex-shrink-0">Hiç detay bilgisi bulunamadı.</p>
)} )}
</> </>
)} )}
</div> </div>
<Dialog.Footer className="flex justify-end items-center pt-3 border-t mt-4">
<Button variant="solid" onClick={handleRunSeed} loading={isLoading}>
{isLoading ? 'Seed Çalıştırılıyor...' : 'Seed İşlemini Başlat'}
</Button>
</Dialog.Footer>
</>
)
}
function BranchSeed({
open,
onDialogClose,
id,
name,
}: {
open: boolean
onDialogClose: () => void
id: string
name: string
}) {
return (
<Container>
<Dialog
width="75%"
height="60%"
isOpen={open}
onClose={onDialogClose}
onRequestClose={onDialogClose}
>
<Dialog.Body className="flex flex-col">
<BranchSeedContent id={id} name={name} />
</Dialog.Body>
</Dialog> </Dialog>
</Container> </Container>
) )

View file

@ -1,6 +1,8 @@
import { useState, useCallback, useMemo, useEffect } from 'react' import { useState, useCallback, useMemo, useEffect } from 'react'
import classNames from 'classnames'
import { createPortal } from 'react-dom' import { createPortal } from 'react-dom'
import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui' import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui'
import { useDialogContext } from '@/components/ui/Dialog/Dialog'
import { import {
FaPlus, FaPlus,
FaTrash, FaTrash,
@ -761,6 +763,15 @@ function generateAlterTableSql(
const STEPS = ['Sütun Tasarımı', 'Entity Ayarları', 'Index / Key', 'İlişkiler', 'T-SQL Önizleme'] as const const STEPS = ['Sütun Tasarımı', 'Entity Ayarları', 'Index / Key', 'İlişkiler', 'T-SQL Önizleme'] as const
type Step = 0 | 1 | 2 | 3 | 4 type Step = 0 | 1 | 2 | 3 | 4
function StepContentWrapper({ children }: { children: React.ReactNode }) {
const { isMaximized } = useDialogContext()
return (
<div className={isMaximized ? 'flex-1 min-h-0 overflow-auto' : 'min-h-[420px]'}>
{children}
</div>
)
}
// ─── Simple Menu Tree (read-only selection) ─────────────────────────────────── // ─── Simple Menu Tree (read-only selection) ───────────────────────────────────
interface SimpleMenuTreeNodeProps { interface SimpleMenuTreeNodeProps {
@ -1646,14 +1657,14 @@ const SqlTableDesignerDialog = ({
<> <>
<div className="flex items-center justify-between py-2"> <div className="flex items-center justify-between py-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button size="sm" variant="solid" color="blue-600" onClick={addFullAuditedColumns}> <Button size="xs" variant="solid" color="blue-600" onClick={addFullAuditedColumns}>
{translate('::App.SqlQueryManager.AddFullAuditedColumns')} {translate('::App.SqlQueryManager.AddFullAuditedColumns')}
</Button> </Button>
<Button size="sm" variant="solid" color="green-600" onClick={addMultiTenantColumns}> <Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
{translate('::App.SqlQueryManager.AddMultiTenantColumns')} {translate('::App.SqlQueryManager.AddMultiTenantColumns')}
</Button> </Button>
<Button <Button
size="sm" size="xs"
variant="solid" variant="solid"
color="amber-600" color="amber-600"
onClick={importColumnsFromRememberedCreateTable} onClick={importColumnsFromRememberedCreateTable}
@ -1664,7 +1675,7 @@ const SqlTableDesignerDialog = ({
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button <Button
size="sm" size="xs"
variant="solid" variant="solid"
color="red-600" color="red-600"
icon={<FaTrash />} icon={<FaTrash />}
@ -1673,7 +1684,7 @@ const SqlTableDesignerDialog = ({
{translate('::App.SqlQueryManager.ClearAllColumns')} {translate('::App.SqlQueryManager.ClearAllColumns')}
</Button> </Button>
<Button <Button
size="sm" size="xs"
variant="solid" variant="solid"
color="blue-600" color="blue-600"
icon={<FaPlus />} icon={<FaPlus />}
@ -2634,9 +2645,9 @@ const SqlTableDesignerDialog = ({
return ( return (
<Dialog isOpen={isOpen} onClose={handleClose} onRequestClose={handleClose} width={900}> <Dialog isOpen={isOpen} onClose={handleClose} onRequestClose={handleClose} width={900}>
<div className="flex flex-col gap-2"> <Dialog.Body className="flex flex-col gap-2">
{/* Header */} {/* Header */}
<div className="flex items-center gap-3 border-b pb-3"> <div className="flex items-center gap-3 border-b pb-3 flex-shrink-0">
<FaTable className="text-2xl text-blue-500" /> <FaTable className="text-2xl text-blue-500" />
<div> <div>
<h5 className="font-bold"> <h5 className="font-bold">
@ -2653,19 +2664,19 @@ const SqlTableDesignerDialog = ({
</div> </div>
{/* Steps */} {/* Steps */}
{renderStepIndicator()} <div className="flex-shrink-0">{renderStepIndicator()}</div>
{/* Content */} {/* Content */}
<div className="min-h-[420px]"> <StepContentWrapper>
{step === 0 && renderColumnDesigner()} {step === 0 && renderColumnDesigner()}
{step === 1 && renderEntitySettings()} {step === 1 && renderEntitySettings()}
{step === 2 && renderIndexes()} {step === 2 && renderIndexes()}
{step === 3 && renderRelationships()} {step === 3 && renderRelationships()}
{step === 4 && renderSqlPreview()} {step === 4 && renderSqlPreview()}
</div> </StepContentWrapper>
</Dialog.Body>
{/* Footer */} <Dialog.Footer className="flex justify-between items-center border-t pt-3 mt-1">
<div className="flex justify-between items-center border-t pt-3 mt-1">
<Button variant="plain" onClick={handleClose}> <Button variant="plain" onClick={handleClose}>
{translate('::Cancel')} {translate('::Cancel')}
</Button> </Button>
@ -2696,8 +2707,7 @@ const SqlTableDesignerDialog = ({
</Button> </Button>
)} )}
</div> </div>
</div> </Dialog.Footer>
</div>
</Dialog> </Dialog>
) )
} }

View file

@ -1,5 +1,7 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import classNames from 'classnames'
import { Button, Input, Dialog, FormContainer, FormItem, Upload, Radio } from '@/components/ui' import { Button, Input, Dialog, FormContainer, FormItem, Upload, Radio } from '@/components/ui'
import { useDialogContext } from '@/components/ui/Dialog/Dialog'
import { FaFileAlt, FaFileUpload, FaPlus, FaTrash } from 'react-icons/fa' import { FaFileAlt, FaFileUpload, FaPlus, FaTrash } from 'react-icons/fa'
import { Field, FieldProps, Form, Formik } from 'formik' import { Field, FieldProps, Form, Formik } from 'formik'
import * as Yup from 'yup' import * as Yup from 'yup'
@ -29,13 +31,18 @@ interface NoteModalProps {
onNoteAdded?: (note: any) => void onNoteAdded?: (note: any) => void
} }
export const NoteModal: React.FC<NoteModalProps> = ({ function NoteModalContent({
entityName, entityName,
entityId, entityId,
isOpen,
onClose, onClose,
onNoteAdded: onActivityAdded, onNoteAdded,
}) => { }: {
entityName: string
entityId: string
onClose: () => void
onNoteAdded?: (note: any) => void
}) {
const { isMaximized } = useDialogContext()
const [uploading, setUploading] = useState(false) const [uploading, setUploading] = useState(false)
const [fileList, setFileList] = useState<File[]>([]) const [fileList, setFileList] = useState<File[]>([])
const { translate } = useLocalization() const { translate } = useLocalization()
@ -57,7 +64,7 @@ export const NoteModal: React.FC<NoteModalProps> = ({
fileList.forEach((file) => formData.append('Files', file)) fileList.forEach((file) => formData.append('Files', file))
const createdActivity = await noteService.create(formData) const createdActivity = await noteService.create(formData)
if (onActivityAdded) onActivityAdded(createdActivity) if (onNoteAdded) onNoteAdded(createdActivity)
setFileList([]) setFileList([])
onClose() onClose()
} catch (err) { } catch (err) {
@ -81,10 +88,9 @@ export const NoteModal: React.FC<NoteModalProps> = ({
} }
return ( return (
<Dialog isOpen={isOpen} onClose={onClose} width={700}> <>
<div className="p-2 w-full mx-auto">
{/* Başlık */} {/* Başlık */}
<div className="flex items-center justify-between mb-5"> <div className="flex items-center justify-between mb-5 flex-shrink-0">
<h3 className="text-xl font-semibold flex items-center gap-3"> <h3 className="text-xl font-semibold flex items-center gap-3">
<div className="p-2 bg-purple-100 rounded-full"> <div className="p-2 bg-purple-100 rounded-full">
<FaPlus className="text-purple-600 text-lg" /> <FaPlus className="text-purple-600 text-lg" />
@ -99,8 +105,11 @@ export const NoteModal: React.FC<NoteModalProps> = ({
onSubmit={handleSave} onSubmit={handleSave}
> >
{({ values, touched, errors, setFieldValue, isSubmitting }) => ( {({ values, touched, errors, setFieldValue, isSubmitting }) => (
<Form> <Form className={classNames('flex flex-col', isMaximized ? 'flex-1 min-h-0' : '')}>
<FormContainer size="sm"> <FormContainer
size="sm"
className={classNames(isMaximized ? 'flex-1 min-h-0 overflow-y-auto' : '')}
>
{/* NOT TİPİ */} {/* 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"> <div className="flex gap-2">
@ -151,7 +160,7 @@ export const NoteModal: React.FC<NoteModalProps> = ({
<HtmlEditor <HtmlEditor
value={field.value} value={field.value}
onValueChanged={(e) => setFieldValue('content', e.value)} onValueChanged={(e) => setFieldValue('content', e.value)}
height={220} height={isMaximized ? '50vh' : 220}
placeholder={translate('::ListForms.ListForm.NoteModal.Content')} placeholder={translate('::ListForms.ListForm.NoteModal.Content')}
> >
<MediaResizing enabled={true} /> <MediaResizing enabled={true} />
@ -258,7 +267,7 @@ export const NoteModal: React.FC<NoteModalProps> = ({
</FormContainer> </FormContainer>
{/* ALT BUTONLAR */} {/* ALT BUTONLAR */}
<div className="mt-5 flex justify-between items-center pt-4 border-t border-gray-200"> <Dialog.Footer className="mt-5 flex justify-between items-center pt-4 border-t border-gray-200">
<Button variant="default" size="md" onClick={onClose} disabled={uploading}> <Button variant="default" size="md" onClick={onClose} disabled={uploading}>
{translate('::Cancel')} {translate('::Cancel')}
</Button> </Button>
@ -271,11 +280,31 @@ export const NoteModal: React.FC<NoteModalProps> = ({
> >
{uploading ? translate('::App.Loading') : translate('::ListForms.Wizard.Add')} {uploading ? translate('::App.Loading') : translate('::ListForms.Wizard.Add')}
</Button> </Button>
</div> </Dialog.Footer>
</Form> </Form>
)} )}
</Formik> </Formik>
</div> </>
)
}
export const NoteModal: React.FC<NoteModalProps> = ({
entityName,
entityId,
isOpen,
onClose,
onNoteAdded,
}) => {
return (
<Dialog isOpen={isOpen} onClose={onClose} width={700}>
<Dialog.Body className="flex flex-col p-2">
<NoteModalContent
entityName={entityName}
entityId={entityId}
onClose={onClose}
onNoteAdded={onNoteAdded}
/>
</Dialog.Body>
</Dialog> </Dialog>
) )
} }

View file

@ -81,7 +81,7 @@ export function AdminView({
const navigationItems = [ const navigationItems = [
{ {
id: 'stats' as AdminSection, id: 'stats' as AdminSection,
label: translate('::App.Videoroom.Dashboard'), label: translate('::App.Forum.Dashboard'),
icon: FaChartBar, icon: FaChartBar,
}, },
{ {

View file

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

View file

@ -165,12 +165,12 @@ export function PostManagement({
{translate('::App.Forum.PostManagement.Title')} {translate('::App.Forum.PostManagement.Title')}
</h2> </h2>
<Button <Button
icon={<FaPlus className="w-4 h-4" />}
variant="solid" variant="solid"
onClick={() => setShowCreateForm(true)} onClick={() => setShowCreateForm(true)}
disabled={loading} 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" 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> <span>{translate('::App.Forum.PostManagement.AddPost')}</span>
</Button> </Button>
</div> </div>
@ -399,15 +399,9 @@ export function PostManagement({
<div className="flex items-center space-x-2 ml-4"> <div className="flex items-center space-x-2 ml-4">
<Button <Button
icon={ size='xs'
post.isAcceptedAnswer ? (
<FaCheckCircle className="w-4 h-4" />
) : (
<FaCircle className="w-4 h-4" />
)
}
onClick={() => handleToggleAcceptedAnswer(post)} onClick={() => handleToggleAcceptedAnswer(post)}
className={`p-2 rounded-lg transition-colors ${ className={`p-1 rounded-lg transition-colors ${
post.isAcceptedAnswer post.isAcceptedAnswer
? 'text-emerald-600 hover:bg-emerald-100' ? 'text-emerald-600 hover:bg-emerald-100'
: 'text-gray-400 hover:bg-gray-100' : 'text-gray-400 hover:bg-gray-100'
@ -417,24 +411,30 @@ export function PostManagement({
? 'Remove Accepted Answer' ? 'Remove Accepted Answer'
: 'Mark as Accepted Answer' : 'Mark as Accepted Answer'
} }
></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')}
> >
{post.isAcceptedAnswer ? (
<FaCheckCircle className="w-3 h-3" />
) : (
<FaCircle className="w-3 h-3" />
)}
</Button> </Button>
<Button <Button
icon={<FaTrashAlt className="w-4 h-4" />} size='xs'
onClick={() => handleEdit(post)}
className="p-1 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title={translate('::App.Forum.PostManagement.EditPost')}
>
<FaEdit className="w-3 h-3" />
</Button>
<Button
size='xs'
onClick={() => confirmDeletePost(post)} onClick={() => confirmDeletePost(post)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors" className="p-1 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title={translate('::App.Forum.PostManagement.DeletePost')} title={translate('::App.Forum.PostManagement.DeletePost')}
> >
<FaTrashAlt className="w-3 h-3" />
</Button> </Button>
</div> </div>
</div> </div>

View file

@ -208,11 +208,11 @@ export function TopicManagement({
</h2> </h2>
<Button <Button
variant="solid" variant="solid"
icon={<FaPlus className="w-4 h-4" />}
onClick={() => setShowCreateForm(true)} onClick={() => setShowCreateForm(true)}
disabled={loading} 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" 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> <span>{translate('::App.Forum.TopicManagement.AddTopic')}</span>
</Button> </Button>
</div> </div>
@ -400,69 +400,73 @@ export function TopicManagement({
<div className="flex items-center space-x-2 ml-4"> <div className="flex items-center space-x-2 ml-4">
<Button <Button
icon={ size='xs'
topic.isPinned ? (
<FaThumbtack className="w-4 h-4" />
) : (
<FaTree className="w-4 h-4" />
)
}
onClick={() => handlePin(topic)} onClick={() => handlePin(topic)}
className={`p-2 rounded-lg transition-colors ${ className={`p-1 rounded-lg transition-colors ${
topic.isPinned topic.isPinned
? 'text-orange-600 hover:bg-orange-100' ? 'text-orange-600 hover:bg-orange-100'
: 'text-gray-400 hover:bg-gray-100' : 'text-gray-400 hover:bg-gray-100'
}`} }`}
title={topic.isPinned ? 'Unpin Topic' : 'Pin Topic'} title={topic.isPinned ? 'Unpin Topic' : 'Pin Topic'}
></Button> >
{topic.isPinned ? (
<FaThumbtack className="w-3 h-3" />
) : (
<FaTree className="w-3 h-3" />
)}
</Button>
<Button <Button
icon={ size='xs'
topic.isLocked ? (
<FaLock className="w-4 h-4" />
) : (
<FaUnlock className="w-4 h-4" />
)
}
onClick={() => handleLock(topic)} onClick={() => handleLock(topic)}
className={`p-2 rounded-lg transition-colors ${ className={`p-1 rounded-lg transition-colors ${
topic.isLocked topic.isLocked
? 'text-yellow-600 hover:bg-yellow-100' ? 'text-yellow-600 hover:bg-yellow-100'
: 'text-green-600 hover:bg-green-100' : 'text-green-600 hover:bg-green-100'
}`} }`}
title={topic.isLocked ? 'Unlock Topic' : 'Lock Topic'} title={topic.isLocked ? 'Unlock Topic' : 'Lock Topic'}
></Button> >
{topic.isLocked ? (
<FaLock className="w-3 h-3" />
) : (
<FaUnlock className="w-3 h-3" />
)}
</Button>
<Button <Button
icon={ size='xs'
topic.isSolved ? (
<FaCheckCircle className="w-4 h-4" />
) : (
<FaCircle className="w-4 h-4" />
)
}
onClick={() => handleSolved(topic)} onClick={() => handleSolved(topic)}
className={`p-2 rounded-lg transition-colors ${ className={`p-1 rounded-lg transition-colors ${
topic.isSolved topic.isSolved
? 'text-emerald-600 hover:bg-emerald-100' ? 'text-emerald-600 hover:bg-emerald-100'
: 'text-gray-400 hover:bg-gray-100' : 'text-gray-400 hover:bg-gray-100'
}`} }`}
title={topic.isSolved ? 'Mark as Unsolved' : 'Mark as Solved'} title={topic.isSolved ? 'Mark as Unsolved' : 'Mark as Solved'}
></Button> >
{topic.isSolved ? (
<FaCheckCircle className="w-3 h-3" />
) : (
<FaCircle className="w-3 h-3" />
)}
</Button>
<Button <Button
icon={<FaEdit className="w-4 h-4" />} size='xs'
onClick={() => handleEdit(topic)} onClick={() => handleEdit(topic)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors" className="p-1 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title={translate('::App.Forum.TopicManagement.EditTopic')} title={translate('::App.Forum.TopicManagement.EditTopic')}
></Button> >
<FaEdit className="w-3 h-3" />
</Button>
<Button <Button
icon={<FaTrashAlt className="w-4 h-4" />} size='xs'
onClick={() => confirmDeleteTopic(topic)} onClick={() => confirmDeleteTopic(topic)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors" className="p-1 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title={translate('::App.Forum.TopicManagement.DeleteTopic')} title={translate('::App.Forum.TopicManagement.DeleteTopic')}
></Button> >
<FaTrashAlt className="w-3 h-3" />
</Button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1476,7 +1476,9 @@ const Grid = (props: GridProps) => {
onClose={() => filterData.setIsImportModalOpen(false)} onClose={() => filterData.setIsImportModalOpen(false)}
onRequestClose={() => filterData.setIsImportModalOpen(false)} onRequestClose={() => filterData.setIsImportModalOpen(false)}
> >
<Dialog.Body className="flex flex-col">
<ImportDashboard gridDto={gridDto} /> <ImportDashboard gridDto={gridDto} />
</Dialog.Body>
</Dialog> </Dialog>
</div> </div>
</> </>

View file

@ -404,11 +404,17 @@ const SchedulerView = (props: SchedulerViewProps) => {
canUpdate: listFormField?.canUpdate ?? false, canUpdate: listFormField?.canUpdate ?? false,
canCreate: listFormField?.canCreate ?? false, canCreate: listFormField?.canCreate ?? false,
canExport: listFormField?.canExport ?? false, canExport: listFormField?.canExport ?? false,
allowEditing: listFormField?.allowEditing ?? true,
allowAdding: listFormField?.allowAdding ?? true,
dataField: i.dataField, dataField: i.dataField,
name: i.dataField, name: i.dataField,
editorType2: i.editorType2, editorType2: i.editorType2,
editorType: editorType:
i.editorType2 == PlatformEditorTypes.dxGridBox ? 'dxDropDownBox' : i.editorType2, i.editorType2 == PlatformEditorTypes.dxGridBox
? 'dxDropDownBox'
: i.editorType2 == PlatformEditorTypes.dxImageUpload
? undefined
: i.editorType2,
colSpan: i.colSpan, colSpan: i.colSpan,
isRequired: i.isRequired, isRequired: i.isRequired,
editorOptions, editorOptions,
@ -419,7 +425,10 @@ const SchedulerView = (props: SchedulerViewProps) => {
item.label = { text: captionize(i.dataField.split(':')[1]) } item.label = { text: captionize(i.dataField.split(':')[1]) }
} }
if ((currentMode == 'edit' && !item.canUpdate) || (currentMode == 'new' && !item.canCreate)) { if (
(currentMode == 'edit' && !item.canUpdate) ||
(currentMode == 'new' && !item.canCreate)
) {
item.editorOptions = { item.editorOptions = {
...item.editorOptions, ...item.editorOptions,
readOnly: true, readOnly: true,
@ -492,18 +501,22 @@ const SchedulerView = (props: SchedulerViewProps) => {
// Tabbed varsa belirli class'lara sahip elementi bul ve flex-direction'ı değiştir // Tabbed varsa belirli class'lara sahip elementi bul ve flex-direction'ı değiştir
if (hasTabbedItems) { if (hasTabbedItems) {
setTimeout(() => { setTimeout(() => {
const targetElement = document.querySelector('.dx-item-content.dx-box-item-content.dx-box-flex.dx-box.dx-widget.dx-collection') const targetElement = document.querySelector(
'.dx-item-content.dx-box-item-content.dx-box-flex.dx-box.dx-widget.dx-collection',
)
if (targetElement) { if (targetElement) {
const htmlElement = targetElement as HTMLElement const htmlElement = targetElement as HTMLElement
// Mevcut style'ı al ve flex-direction: row'u column'a çevir // Mevcut style'ı al ve flex-direction: row'u column'a çevir
const currentStyle = htmlElement.getAttribute('style') || '' const currentStyle = htmlElement.getAttribute('style') || ''
const updatedStyle = currentStyle.replace(/flex-direction:\s*row/gi, 'flex-direction: column') const updatedStyle = currentStyle.replace(
/flex-direction:\s*row/gi,
'flex-direction: column',
)
htmlElement.setAttribute('style', updatedStyle) htmlElement.setAttribute('style', updatedStyle)
} }
}, 100) }, 100)
} }
}, },
[gridDto, translate, isPopupFullScreen, listFormCode], [gridDto, translate, isPopupFullScreen, listFormCode],
) )
@ -540,7 +553,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
key={`Scheduler-${listFormCode}-${schedulerDataSource ? 'loaded' : 'loading'}`} key={`Scheduler-${listFormCode}-${schedulerDataSource ? 'loaded' : 'loading'}`}
id={'Scheduler-' + listFormCode} id={'Scheduler-' + listFormCode}
dataSource={schedulerDataSource} dataSource={schedulerDataSource}
dateSerializationFormat='yyyy-MM-ddTHH:mm:ss' dateSerializationFormat="yyyy-MM-ddTHH:mm:ss"
textExpr={gridDto.gridOptions.schedulerOptionDto?.textExpr || 'text'} textExpr={gridDto.gridOptions.schedulerOptionDto?.textExpr || 'text'}
startDateExpr={gridDto.gridOptions.schedulerOptionDto?.startDateExpr || 'startDate'} startDateExpr={gridDto.gridOptions.schedulerOptionDto?.startDateExpr || 'startDate'}
endDateExpr={gridDto.gridOptions.schedulerOptionDto?.endDateExpr || 'endDate'} endDateExpr={gridDto.gridOptions.schedulerOptionDto?.endDateExpr || 'endDate'}

View file

@ -1068,12 +1068,16 @@ const Tree = (props: TreeProps) => {
canUpdate: listFormField?.canUpdate ?? false, canUpdate: listFormField?.canUpdate ?? false,
canCreate: listFormField?.canCreate ?? false, canCreate: listFormField?.canCreate ?? false,
canExport: listFormField?.canExport ?? false, canExport: listFormField?.canExport ?? false,
allowEditing: listFormField?.allowEditing ?? true,
allowAdding: listFormField?.allowAdding ?? true,
dataField: i.dataField, dataField: i.dataField,
name: i.dataField, name: i.dataField,
editorType2: i.editorType2, editorType2: i.editorType2,
editorType: editorType:
i.editorType2 == PlatformEditorTypes.dxGridBox i.editorType2 == PlatformEditorTypes.dxGridBox
? 'dxDropDownBox' ? 'dxDropDownBox'
: i.editorType2 == PlatformEditorTypes.dxImageUpload
? undefined
: i.editorType2, : i.editorType2,
colSpan: i.colSpan, colSpan: i.colSpan,
isRequired: i.isRequired, isRequired: i.isRequired,