Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 62 additions & 1 deletion backend/src/controllers/coreControllers/adminController/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,63 @@
const createUserController = require('@/controllers/middlewaresControllers/createUserController');
module.exports = createUserController('Admin');
const createCRUDController = require('@/controllers/middlewaresControllers/createCRUDController');
const mongoose = require('mongoose');

// Create base controllers
const userController = createUserController('Admin');
const crudMethods = createCRUDController('Admin');

// Custom delete function to prevent owner deletion
const customDelete = async (req, res) => {
const Admin = mongoose.model('Admin');

// Validate MongoDB ObjectId
if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
return res.status(400).json({
success: false,
result: null,
message: 'Invalid ID format',
});
}

const objectId = new mongoose.Types.ObjectId(req.params.id);

// Check if admin exists and get their role
const admin = await Admin.findOne({ _id: objectId, removed: false }).exec();

if (!admin) {
return res.status(404).json({
success: false,
result: null,
message: 'No document found',
});
}

// Prevent deletion of owner accounts
if (admin.role === 'owner') {
return res.status(403).json({
success: false,
result: null,
message: 'Owner accounts cannot be deleted',
});
}

// Proceed with soft delete
const result = await Admin.findOneAndUpdate(
{ _id: objectId },
{ $set: { removed: true } },
{ new: true }
).exec();

return res.status(200).json({
success: true,
result,
message: 'Successfully deleted the document',
});
};

// Merge all methods and override delete
module.exports = {
...userController,
...crudMethods,
delete: customDelete,
};
6 changes: 6 additions & 0 deletions backend/src/routes/coreRoutes/coreApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ const { singleStorageUpload } = require('@/middlewares/uploadMiddleware');

// //_______________________________ Admin management_______________________________

router.route('/admin/create').post(catchErrors(adminController.create));
router.route('/admin/read/:id').get(catchErrors(adminController.read));
router.route('/admin/update/:id').patch(catchErrors(adminController.update));
router.route('/admin/delete/:id').delete(catchErrors(adminController.delete));
router.route('/admin/search').get(catchErrors(adminController.search));
router.route('/admin/list').get(catchErrors(adminController.list));
router.route('/admin/listAll').get(catchErrors(adminController.listAll));

router.route('/admin/password-update/:id').patch(catchErrors(adminController.updatePassword));

Expand Down
5 changes: 5 additions & 0 deletions frontend/src/apps/Navigation/NavigationContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ function Sidebar({ collapsible, isMobile = false }) {
icon: <CustomerServiceOutlined />,
label: <Link to={'/customer'}>{translate('customers')}</Link>,
},
{
key: 'admin',
icon: <UserOutlined />,
label: <Link to={'/admin'}>{translate('admin')}</Link>,
},

{
key: 'invoice',
Expand Down
52 changes: 51 additions & 1 deletion frontend/src/components/DataTable/DataTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,56 @@ export default function DataTable({ config, extra = [] }) {
const { moneyFormatter } = useMoney();
const { dateFormat } = useDate();

// Function to generate menu items based on record
const getMenuItems = (record) => {
const baseItems = [
{
label: translate('Show'),
key: 'read',
icon: <EyeOutlined />,
},
{
label: translate('Edit'),
key: 'edit',
icon: <EditOutlined />,
},
...extra,
];

// For admin entity, check if the record is an owner
const isOwner = entity === 'admin' && record?.role === 'owner';

if (isOwner) {
// Add divider and disabled delete option for owner with tooltip
return [
...baseItems,
{
type: 'divider',
},
{
label: translate('Delete'),
key: 'delete',
icon: <DeleteOutlined />,
disabled: true,
title: translate('owner_accounts_cannot_be_deleted'),
},
];
}

// Normal delete for non-owner records
return [
...baseItems,
{
type: 'divider',
},
{
label: translate('Delete'),
key: 'delete',
icon: <DeleteOutlined />,
},
];
};

const items = [
{
label: translate('Show'),
Expand Down Expand Up @@ -112,7 +162,7 @@ export default function DataTable({ config, extra = [] }) {
render: (_, record) => (
<Dropdown
menu={{
items,
items: getMenuItems(record),
onClick: ({ key }) => {
switch (key) {
case 'read':
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locale/translation/en_us.js
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ const lang = {
currencies: 'Currencies',
payments_mode: 'Payments Mode',
account_owner: 'Account Owner',
owner_accounts_cannot_be_deleted: 'Owner accounts cannot be deleted',
create_only: 'Create Only',
enter_code: 'Enter Code',
offers: 'Offers',
Expand Down
83 changes: 83 additions & 0 deletions frontend/src/pages/Admin/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { ErpLayout } from '@/layout';
import CrudModule from '@/modules/CrudModule/CrudModule';
import AdminForm from '@/forms/AdminForm';

import useLanguage from '@/locale/useLanguage';

export default function Admin() {
const translate = useLanguage();
const entity = 'admin';
const searchConfig = {
displayLabels: ['name', 'surname'],
searchFields: 'name,surname,email',
outputValue: '_id',
};

const deleteModalLabels = ['name', 'surname', 'email'];

const dataTableColumns = [
{
title: translate('name'),
dataIndex: 'name',
},
{
title: translate('surname'),
dataIndex: 'surname',
},
{
title: translate('email'),
dataIndex: 'email',
},
{
title: translate('role'),
dataIndex: 'role',
},
{
title: translate('enabled'),
dataIndex: 'enabled',
render: (enabled) => (enabled ? translate('Yes') : translate('No')),
},
];

const readColumns = [
{
title: translate('name'),
dataIndex: 'name',
},
{
title: translate('surname'),
dataIndex: 'surname',
},
{
title: translate('email'),
dataIndex: 'email',
},
{
title: translate('role'),
dataIndex: 'role',
},
{
title: translate('enabled'),
dataIndex: 'enabled',
render: (enabled) => (enabled ? translate('Yes') : translate('No')),
},
];

const config = {
entity,
PANEL_TITLE: translate('admin'),
DATATABLE_TITLE: translate('admin_list'),
ADD_NEW_ENTITY: translate('add_new_admin'),
ENTITY_NAME: translate('admin'),
dataTableColumns,
readColumns,
searchConfig,
deleteModalLabels,
};

return (
<ErpLayout>
<CrudModule config={config} createForm={<AdminForm />} updateForm={<AdminForm isUpdateForm={true} />} />
</ErpLayout>
);
}
5 changes: 5 additions & 0 deletions frontend/src/router/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const NotFound = lazy(() => import('@/pages/NotFound.jsx'));

const Dashboard = lazy(() => import('@/pages/Dashboard'));
const Customer = lazy(() => import('@/pages/Customer'));
const Admin = lazy(() => import('@/pages/Admin'));
const Invoice = lazy(() => import('@/pages/Invoice'));
const InvoiceCreate = lazy(() => import('@/pages/Invoice/InvoiceCreate'));

Expand Down Expand Up @@ -52,6 +53,10 @@ let routes = {
path: '/customer',
element: <Customer />,
},
{
path: '/admin',
element: <Admin />,
},

{
path: '/invoice',
Expand Down