Compare commits

...

5 Commits

Author SHA1 Message Date
Flatlogic Bot
8588cf351b Updated via schema editor on 2025-07-17 10:52 2025-07-17 10:53:13 +00:00
Flatlogic Bot
efd7c4a158 Updated via schema editor on 2025-07-17 10:01 2025-07-17 10:02:36 +00:00
Flatlogic Bot
b319d5a8fa v3.1 2025-07-17 09:54:32 +00:00
Flatlogic Bot
175e75ff05 Updated via schema editor on 2025-07-17 09:48 2025-07-17 09:48:56 +00:00
Flatlogic Bot
898a1a5bd3 Updated via schema editor on 2025-07-17 09:30 2025-07-17 09:30:45 +00:00
38 changed files with 1139 additions and 195 deletions

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
node_modules/
*/node_modules/
*/build/
**/node_modules/
**/build/
.DS_Store
.env

File diff suppressed because one or more lines are too long

View File

@ -15,10 +15,15 @@ module.exports = class AssetsDBApi {
{
id: data.id || undefined,
asset_name: data.asset_name || null,
asset_make: data.asset_make || null,
asset_type: data.asset_type || null,
purchase_date: data.purchase_date || null,
maintenance_due_date: data.maintenance_due_date || null,
asset_po: data.asset_po || null,
asset_eol: data.asset_eol || null,
asset_purchase_price: data.asset_purchase_price || null,
asset_model: data.asset_model || null,
asset_tag: data.asset_tag || null,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
@ -41,10 +46,15 @@ module.exports = class AssetsDBApi {
const assetsData = data.map((item, index) => ({
id: item.id || undefined,
asset_name: item.asset_name || null,
asset_make: item.asset_make || null,
asset_type: item.asset_type || null,
purchase_date: item.purchase_date || null,
maintenance_due_date: item.maintenance_due_date || null,
asset_po: item.asset_po || null,
asset_eol: item.asset_eol || null,
asset_purchase_price: item.asset_purchase_price || null,
asset_model: item.asset_model || null,
asset_tag: item.asset_tag || null,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
@ -67,8 +77,8 @@ module.exports = class AssetsDBApi {
const updatePayload = {};
if (data.asset_name !== undefined)
updatePayload.asset_name = data.asset_name;
if (data.asset_make !== undefined)
updatePayload.asset_make = data.asset_make;
if (data.asset_type !== undefined)
updatePayload.asset_type = data.asset_type;
@ -79,6 +89,18 @@ module.exports = class AssetsDBApi {
if (data.maintenance_due_date !== undefined)
updatePayload.maintenance_due_date = data.maintenance_due_date;
if (data.asset_po !== undefined) updatePayload.asset_po = data.asset_po;
if (data.asset_eol !== undefined) updatePayload.asset_eol = data.asset_eol;
if (data.asset_purchase_price !== undefined)
updatePayload.asset_purchase_price = data.asset_purchase_price;
if (data.asset_model !== undefined)
updatePayload.asset_model = data.asset_model;
if (data.asset_tag !== undefined) updatePayload.asset_tag = data.asset_tag;
updatePayload.updatedById = currentUser.id;
await assets.update(updatePayload, { transaction });
@ -207,28 +229,42 @@ module.exports = class AssetsDBApi {
};
}
if (filter.asset_name) {
if (filter.asset_make) {
where = {
...where,
[Op.and]: Utils.ilike('assets', 'asset_name', filter.asset_name),
[Op.and]: Utils.ilike('assets', 'asset_make', filter.asset_make),
};
}
if (filter.calendarStart && filter.calendarEnd) {
if (filter.asset_po) {
where = {
...where,
[Op.or]: [
{
purchase_date: {
[Op.between]: [filter.calendarStart, filter.calendarEnd],
},
},
{
maintenance_due_date: {
[Op.between]: [filter.calendarStart, filter.calendarEnd],
},
},
],
[Op.and]: Utils.ilike('assets', 'asset_po', filter.asset_po),
};
}
if (filter.asset_purchase_price) {
where = {
...where,
[Op.and]: Utils.ilike(
'assets',
'asset_purchase_price',
filter.asset_purchase_price,
),
};
}
if (filter.asset_model) {
where = {
...where,
[Op.and]: Utils.ilike('assets', 'asset_model', filter.asset_model),
};
}
if (filter.asset_tag) {
where = {
...where,
[Op.and]: Utils.ilike('assets', 'asset_tag', filter.asset_tag),
};
}
@ -280,6 +316,30 @@ module.exports = class AssetsDBApi {
}
}
if (filter.asset_eolRange) {
const [start, end] = filter.asset_eolRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
asset_eol: {
...where.asset_eol,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
asset_eol: {
...where.asset_eol,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
@ -356,22 +416,22 @@ module.exports = class AssetsDBApi {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike('assets', 'asset_name', query),
Utils.ilike('assets', 'asset_make', query),
],
};
}
const records = await db.assets.findAll({
attributes: ['id', 'asset_name'],
attributes: ['id', 'asset_make'],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['asset_name', 'ASC']],
orderBy: [['asset_make', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.asset_name,
label: record.asset_make,
}));
}
};

View File

@ -226,24 +226,6 @@ module.exports = class Compliance_certificatesDBApi {
};
}
if (filter.calendarStart && filter.calendarEnd) {
where = {
...where,
[Op.or]: [
{
issue_date: {
[Op.between]: [filter.calendarStart, filter.calendarEnd],
},
},
{
expiry_date: {
[Op.between]: [filter.calendarStart, filter.calendarEnd],
},
},
],
};
}
if (filter.issue_dateRange) {
const [start, end] = filter.issue_dateRange;

View File

@ -0,0 +1,71 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(
'assets',
'asset_po',
{
type: Sequelize.DataTypes.TEXT,
},
{ transaction },
);
await queryInterface.addColumn(
'assets',
'asset_eol',
{
type: Sequelize.DataTypes.DATE,
},
{ transaction },
);
await queryInterface.addColumn(
'assets',
'asset_purchase_price',
{
type: Sequelize.DataTypes.TEXT,
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('assets', 'asset_purchase_price', {
transaction,
});
await queryInterface.removeColumn('assets', 'asset_eol', { transaction });
await queryInterface.removeColumn('assets', 'asset_po', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -0,0 +1,72 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.createTable(
'reports',
{
id: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.DataTypes.UUIDV4,
primaryKey: true,
},
createdById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
updatedById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
createdAt: { type: Sequelize.DataTypes.DATE },
updatedAt: { type: Sequelize.DataTypes.DATE },
deletedAt: { type: Sequelize.DataTypes.DATE },
importHash: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.dropTable('reports', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -0,0 +1,72 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.dropTable('reports', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.createTable(
'reports',
{
id: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.DataTypes.UUIDV4,
primaryKey: true,
},
createdById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
updatedById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
createdAt: { type: Sequelize.DataTypes.DATE },
updatedAt: { type: Sequelize.DataTypes.DATE },
deletedAt: { type: Sequelize.DataTypes.DATE },
importHash: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -0,0 +1,68 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.renameColumn('assets', 'asset_name', 'asset_make', {
transaction,
});
await queryInterface.addColumn(
'assets',
'asset_model',
{
type: Sequelize.DataTypes.TEXT,
},
{ transaction },
);
await queryInterface.addColumn(
'assets',
'asset_tag',
{
type: Sequelize.DataTypes.TEXT,
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('assets', 'asset_tag', { transaction });
await queryInterface.removeColumn('assets', 'asset_model', {
transaction,
});
await queryInterface.renameColumn('assets', 'asset_make', 'asset_name', {
transaction,
});
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -14,7 +14,7 @@ module.exports = function (sequelize, DataTypes) {
primaryKey: true,
},
asset_name: {
asset_make: {
type: DataTypes.TEXT,
},
@ -32,6 +32,26 @@ module.exports = function (sequelize, DataTypes) {
type: DataTypes.DATE,
},
asset_po: {
type: DataTypes.TEXT,
},
asset_eol: {
type: DataTypes.DATE,
},
asset_purchase_price: {
type: DataTypes.TEXT,
},
asset_model: {
type: DataTypes.TEXT,
},
asset_tag: {
type: DataTypes.TEXT,
},
importHash: {
type: DataTypes.STRING(255),
allowNull: true,

View File

@ -13,43 +13,73 @@ const SoftwareLicenses = db.software_licenses;
const AssetsData = [
{
asset_name: 'Dell Laptop',
asset_make: 'Francis Crick',
asset_type: 'Hardware',
asset_type: 'Software',
// type code here for "relation_one" field
purchase_date: new Date('2022-01-15T00:00:00Z'),
maintenance_due_date: new Date('2023-01-15T00:00:00Z'),
asset_po: 'Anton van Leeuwenhoek',
asset_eol: new Date(Date.now()),
asset_purchase_price: 'Lucretius',
asset_model: 'Galileo Galilei',
asset_tag: 'Charles Darwin',
},
{
asset_name: 'HP Printer',
asset_make: 'Stephen Hawking',
asset_type: 'Hardware',
asset_type: 'Software',
// type code here for "relation_one" field
purchase_date: new Date('2021-06-10T00:00:00Z'),
maintenance_due_date: new Date('2022-06-10T00:00:00Z'),
asset_po: 'Dmitri Mendeleev',
asset_eol: new Date(Date.now()),
asset_purchase_price: 'William Bayliss',
asset_model: 'Dmitri Mendeleev',
asset_tag: 'Pierre Simon de Laplace',
},
{
asset_name: 'Microsoft 365',
asset_make: 'Franz Boas',
asset_type: 'Hardware',
asset_type: 'Software',
// type code here for "relation_one" field
purchase_date: new Date('2022-03-01T00:00:00Z'),
maintenance_due_date: new Date('2023-03-01T00:00:00Z'),
asset_po: 'Ernest Rutherford',
asset_eol: new Date(Date.now()),
asset_purchase_price: 'James Clerk Maxwell',
asset_model: 'Hans Bethe',
asset_tag: 'Erwin Schrodinger',
},
{
asset_name: 'Salesforce License',
asset_make: 'Neils Bohr',
asset_type: 'Hardware',
@ -58,10 +88,20 @@ const AssetsData = [
purchase_date: new Date('2021-11-20T00:00:00Z'),
maintenance_due_date: new Date('2022-11-20T00:00:00Z'),
asset_po: 'J. Robert Oppenheimer',
asset_eol: new Date(Date.now()),
asset_purchase_price: 'John Bardeen',
asset_model: 'Max Born',
asset_tag: 'Jonas Salk',
},
{
asset_name: 'MacBook Pro',
asset_make: 'Emil Kraepelin',
asset_type: 'Software',
@ -70,6 +110,16 @@ const AssetsData = [
purchase_date: new Date('2022-05-05T00:00:00Z'),
maintenance_due_date: new Date('2023-05-05T00:00:00Z'),
asset_po: 'J. Robert Oppenheimer',
asset_eol: new Date(Date.now()),
asset_purchase_price: 'B. F. Skinner',
asset_model: 'Ernst Haeckel',
asset_tag: 'Johannes Kepler',
},
];
@ -145,13 +195,13 @@ const DepartmentsData = [
},
{
name: 'B. F. Skinner',
name: 'Lynn Margulis',
// type code here for "relation_many" field
},
{
name: 'Isaac Newton',
name: 'Edward O. Wilson',
// type code here for "relation_many" field
},
@ -181,7 +231,7 @@ const EmployeesData = [
// type code here for "relation_one" field
status: 'Active',
status: 'Inactive',
// type code here for "relation_one" field
},
@ -209,7 +259,7 @@ const EmployeesData = [
// type code here for "relation_one" field
status: 'Inactive',
status: 'Active',
// type code here for "relation_one" field
},
@ -223,7 +273,7 @@ const EmployeesData = [
// type code here for "relation_one" field
status: 'Inactive',
status: 'Active',
// type code here for "relation_one" field
},
@ -233,7 +283,7 @@ const SoftwareLicensesData = [
{
software_name: 'Microsoft 365',
license_type: 'Salesforce',
license_type: 'Microsoft365',
expiry_date: new Date('2023-12-31T00:00:00Z'),
},
@ -257,7 +307,7 @@ const SoftwareLicensesData = [
{
software_name: 'Slack',
license_type: 'Salesforce',
license_type: 'Microsoft365',
expiry_date: new Date('2023-11-20T00:00:00Z'),
},

View File

@ -0,0 +1,87 @@
const { v4: uuid } = require('uuid');
const db = require('../models');
const Sequelize = require('sequelize');
const config = require('../../config');
module.exports = {
/**
* @param{import("sequelize").QueryInterface} queryInterface
* @return {Promise<void>}
*/
async up(queryInterface) {
const createdAt = new Date();
const updatedAt = new Date();
/** @type {Map<string, string>} */
const idMap = new Map();
/**
* @param {string} key
* @return {string}
*/
function getId(key) {
if (idMap.has(key)) {
return idMap.get(key);
}
const id = uuid();
idMap.set(key, id);
return id;
}
/**
* @param {string} name
*/
function createPermissions(name) {
return [
{
id: getId(`CREATE_${name.toUpperCase()}`),
createdAt,
updatedAt,
name: `CREATE_${name.toUpperCase()}`,
},
{
id: getId(`READ_${name.toUpperCase()}`),
createdAt,
updatedAt,
name: `READ_${name.toUpperCase()}`,
},
{
id: getId(`UPDATE_${name.toUpperCase()}`),
createdAt,
updatedAt,
name: `UPDATE_${name.toUpperCase()}`,
},
{
id: getId(`DELETE_${name.toUpperCase()}`),
createdAt,
updatedAt,
name: `DELETE_${name.toUpperCase()}`,
},
];
}
const entities = ['reports'];
const createdPermissions = entities.flatMap(createPermissions);
// Add permissions to database
await queryInterface.bulkInsert('permissions', createdPermissions);
// Get permissions ids
const permissionsIds = createdPermissions.map((p) => p.id);
// Get admin role
const adminRole = await db.roles.findOne({
where: { name: config.roles.admin },
});
if (adminRole) {
// Add permissions to admin role if it exists
await adminRole.addPermissions(permissionsIds);
}
},
down: async (queryInterface, Sequelize) => {
await queryInterface.bulkDelete(
'permissions',
entities.flatMap(createPermissions),
);
},
};

View File

@ -20,9 +20,21 @@ router.use(checkCrudPermissions('assets'));
* type: object
* properties:
* asset_name:
* asset_make:
* type: string
* default: asset_name
* default: asset_make
* asset_po:
* type: string
* default: asset_po
* asset_purchase_price:
* type: string
* default: asset_purchase_price
* asset_model:
* type: string
* default: asset_model
* asset_tag:
* type: string
* default: asset_tag
*
*/
@ -302,10 +314,15 @@ router.get(
if (filetype && filetype === 'csv') {
const fields = [
'id',
'asset_name',
'asset_make',
'asset_po',
'asset_purchase_price',
'asset_model',
'asset_tag',
'purchase_date',
'maintenance_due_date',
'asset_eol',
];
const opts = { fields };
try {

View File

@ -43,7 +43,17 @@ module.exports = class SearchService {
const tableColumns = {
users: ['firstName', 'lastName', 'phoneNumber', 'email'],
assets: ['asset_name'],
assets: [
'asset_make',
'asset_po',
'asset_purchase_price',
'asset_model',
'asset_tag',
],
compliance_certificates: ['certificate_name'],

View File

@ -0,0 +1 @@
{}

View File

@ -62,7 +62,7 @@ const CardAssets = ({
href={`/assets/assets-view/?id=${item.id}`}
className='text-lg font-bold leading-6 line-clamp-1'
>
{item.asset_name}
{item.asset_make}
</Link>
<div className='ml-auto '>
@ -78,11 +78,11 @@ const CardAssets = ({
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
AssetName
Asset Make
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.asset_name}
{item.asset_make}
</div>
</dd>
</div>
@ -134,6 +134,61 @@ const CardAssets = ({
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
PO Number
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.asset_po}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Asset Eol
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{dataFormatter.dateTimeFormatter(item.asset_eol)}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Asset Purchase Price
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.asset_purchase_price}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Asset Model
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.asset_model}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Asset Tag
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.asset_tag}
</div>
</dd>
</div>
</dl>
</li>
))}

View File

@ -52,8 +52,8 @@ const ListAssets = ({
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>AssetName</p>
<p className={'line-clamp-2'}>{item.asset_name}</p>
<p className={'text-xs text-gray-500 '}>Asset Make</p>
<p className={'line-clamp-2'}>{item.asset_make}</p>
</div>
<div className={'flex-1 px-3'}>
@ -87,6 +87,37 @@ const ListAssets = ({
)}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>PO Number</p>
<p className={'line-clamp-2'}>{item.asset_po}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Asset Eol</p>
<p className={'line-clamp-2'}>
{dataFormatter.dateTimeFormatter(item.asset_eol)}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>
Asset Purchase Price
</p>
<p className={'line-clamp-2'}>
{item.asset_purchase_price}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Asset Model</p>
<p className={'line-clamp-2'}>{item.asset_model}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Asset Tag</p>
<p className={'line-clamp-2'}>{item.asset_tag}</p>
</div>
</Link>
<ListActionsPopover
onDelete={onDelete}

View File

@ -20,10 +20,7 @@ import _ from 'lodash';
import dataFormatter from '../../helpers/dataFormatter';
import { dataGridStyles } from '../../styles';
import BigCalendar from '../BigCalendar';
import { SlotInfo } from 'react-big-calendar';
const perPage = 100;
const perPage = 10;
const TableSampleAssets = ({
filterItems,
@ -101,12 +98,6 @@ const TableSampleAssets = ({
setIsModalTrashActive(false);
};
const handleCreateEventAction = ({ start, end }: SlotInfo) => {
router.push(
`/assets/assets-new?dateRangeStart=${start.toISOString()}&dateRangeEnd=${end.toISOString()}`,
);
};
const handleDeleteModalAction = (id: string) => {
setId(id);
setIsModalTrashActive(true);
@ -470,27 +461,7 @@ const TableSampleAssets = ({
<p>Are you sure you want to delete this item?</p>
</CardBoxModal>
{!showGrid && (
<BigCalendar
events={assets}
showField={'asset_name'}
start-data-key={'purchase_date'}
end-data-key={'maintenance_due_date'}
handleDeleteAction={handleDeleteModalAction}
pathEdit={`/assets/assets-edit/?id=`}
pathView={`/assets/assets-view/?id=`}
handleCreateEventAction={handleCreateEventAction}
onDateRangeChange={(range) => {
loadData(
0,
`&calendarStart=${range.start}&calendarEnd=${range.end}`,
);
}}
entityName={'assets'}
/>
)}
{showGrid && dataGrid}
{dataGrid}
{selectedRows.length > 0 &&
createPortal(

View File

@ -39,8 +39,8 @@ export const loadColumns = async (
return [
{
field: 'asset_name',
headerName: 'AssetName',
field: 'asset_make',
headerName: 'Asset Make',
flex: 1,
minWidth: 120,
filterable: false,
@ -114,6 +114,70 @@ export const loadColumns = async (
new Date(params.row.maintenance_due_date),
},
{
field: 'asset_po',
headerName: 'PO Number',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'asset_eol',
headerName: 'Asset Eol',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'dateTime',
valueGetter: (params: GridValueGetterParams) =>
new Date(params.row.asset_eol),
},
{
field: 'asset_purchase_price',
headerName: 'Asset Purchase Price',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'asset_model',
headerName: 'Asset Model',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'asset_tag',
headerName: 'Asset Tag',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'actions',
type: 'actions',

View File

@ -20,10 +20,7 @@ import _ from 'lodash';
import dataFormatter from '../../helpers/dataFormatter';
import { dataGridStyles } from '../../styles';
import BigCalendar from '../BigCalendar';
import { SlotInfo } from 'react-big-calendar';
const perPage = 100;
const perPage = 10;
const TableSampleCompliance_certificates = ({
filterItems,
@ -104,12 +101,6 @@ const TableSampleCompliance_certificates = ({
setIsModalTrashActive(false);
};
const handleCreateEventAction = ({ start, end }: SlotInfo) => {
router.push(
`/compliance_certificates/compliance_certificates-new?dateRangeStart=${start.toISOString()}&dateRangeEnd=${end.toISOString()}`,
);
};
const handleDeleteModalAction = (id: string) => {
setId(id);
setIsModalTrashActive(true);
@ -475,27 +466,7 @@ const TableSampleCompliance_certificates = ({
<p>Are you sure you want to delete this item?</p>
</CardBoxModal>
{!showGrid && (
<BigCalendar
events={compliance_certificates}
showField={'certificate_name'}
start-data-key={'issue_date'}
end-data-key={'expiry_date'}
handleDeleteAction={handleDeleteModalAction}
pathEdit={`/compliance_certificates/compliance_certificates-edit/?id=`}
pathView={`/compliance_certificates/compliance_certificates-view/?id=`}
handleCreateEventAction={handleCreateEventAction}
onDateRangeChange={(range) => {
loadData(
0,
`&calendarStart=${range.start}&calendarEnd=${range.end}`,
);
}}
entityName={'compliance_certificates'}
/>
)}
{showGrid && dataGrid}
{dataGrid}
{selectedRows.length > 0 &&
createPortal(

View File

@ -17,7 +17,7 @@ export default function WebSiteFooter({ projectName }: WebSiteFooterProps) {
const borders = useAppSelector((state) => state.style.borders);
const websiteHeder = useAppSelector((state) => state.style.websiteHeder);
const style = FooterStyle.WITH_PROJECT_NAME;
const style = FooterStyle.WITH_PAGES;
const design = FooterDesigns.DEFAULT_DESIGN;

View File

@ -17,7 +17,7 @@ export default function WebSiteHeader({ projectName }: WebSiteHeaderProps) {
const websiteHeder = useAppSelector((state) => state.style.websiteHeder);
const borders = useAppSelector((state) => state.style.borders);
const style = HeaderStyle.PAGES_RIGHT;
const style = HeaderStyle.PAGES_LEFT;
const design = HeaderDesigns.DEFAULT_DESIGN;
return (

View File

@ -92,6 +92,32 @@ const menuAside: MenuAsideItem[] = [
label: 'Profile',
icon: icon.mdiAccountCircle,
},
// Reporting Links
{
href: '/assets-assigned',
label: 'Assets Assigned by User',
icon: icon.mdiAccountMultipleOutline ?? icon.mdiTable,
permissions: 'READ_ASSETS',
},
{
href: '/assets-available',
label: 'Assets Available',
icon: icon.mdiStorage ?? icon.mdiTable,
permissions: 'READ_ASSETS',
},
{
href: '/assets-in-stock',
label: 'Assets in Stock',
icon: icon.mdiCubeOutline ?? icon.mdiTable,
permissions: 'READ_ASSETS',
},
{
href: '/employees-status',
label: 'Employees (Active/Inactive)',
icon: icon.mdiAccountCircle ?? icon.mdiTable,
permissions: 'READ_EMPLOYEES',
},
{
href: '/home',

View File

@ -0,0 +1,32 @@
import React, { ReactElement } from 'react';
import Head from 'next/head';
import { mdiChartTimelineVariant } from '@mdi/js';
import { getPageTitle } from '../config';
import LayoutAuthenticated from '../layouts/Authenticated';
import SectionMain from '../components/SectionMain';
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
import CardBox from '../components/CardBox';
import TableAssets from '../components/Assets/TableAssets';
const AssetsAssignedReport = () => (
<>
<Head>
<title>{getPageTitle('Assets Assigned by User')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title="Assets Assigned by User"
/>
<CardBox className="mb-6" hasTable>
<TableAssets showGrid={false} />
</CardBox>
</SectionMain>
</>
);
AssetsAssignedReport.getLayout = (page: ReactElement) => (
<LayoutAuthenticated permission="READ_ASSETS">{page}</LayoutAuthenticated>
);
export default AssetsAssignedReport;

View File

@ -0,0 +1,32 @@
import React, { ReactElement } from 'react';
import Head from 'next/head';
import { mdiChartTimelineVariant } from '@mdi/js';
import { getPageTitle } from '../config';
import LayoutAuthenticated from '../layouts/Authenticated';
import SectionMain from '../components/SectionMain';
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
import CardBox from '../components/CardBox';
import TableAssets from '../components/Assets/TableAssets';
const AssetsAvailableReport = () => (
<>
<Head>
<title>{getPageTitle('Assets Available')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title="Assets Available"
/>
<CardBox className="mb-6" hasTable>
<TableAssets showGrid={false} />
</CardBox>
</SectionMain>
</>
);
AssetsAvailableReport.getLayout = (page: ReactElement) => (
<LayoutAuthenticated permission="READ_ASSETS">{page}</LayoutAuthenticated>
);
export default AssetsAvailableReport;

View File

@ -0,0 +1,32 @@
import React, { ReactElement } from 'react';
import Head from 'next/head';
import { mdiChartTimelineVariant } from '@mdi/js';
import { getPageTitle } from '../config';
import LayoutAuthenticated from '../layouts/Authenticated';
import SectionMain from '../components/SectionMain';
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
import CardBox from '../components/CardBox';
import TableAssets from '../components/Assets/TableAssets';
const AssetsInStockReport = () => (
<>
<Head>
<title>{getPageTitle('Assets in Stock')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title="Assets in Stock"
/>
<CardBox className="mb-6" hasTable>
<TableAssets showGrid={false} />
</CardBox>
</SectionMain>
</>
);
AssetsInStockReport.getLayout = (page: ReactElement) => (
<LayoutAuthenticated permission="READ_ASSETS">{page}</LayoutAuthenticated>
);
export default AssetsInStockReport;

View File

@ -36,7 +36,7 @@ const EditAssets = () => {
const router = useRouter();
const dispatch = useAppDispatch();
const initVals = {
asset_name: '',
asset_make: '',
asset_type: '',
@ -45,6 +45,16 @@ const EditAssets = () => {
purchase_date: new Date(),
maintenance_due_date: new Date(),
asset_po: '',
asset_eol: new Date(),
asset_purchase_price: '',
asset_model: '',
asset_tag: '',
};
const [initialValues, setInitialValues] = useState(initVals);
@ -97,8 +107,8 @@ const EditAssets = () => {
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField label='AssetName'>
<Field name='asset_name' placeholder='AssetName' />
<FormField label='Asset Make'>
<Field name='asset_make' placeholder='Asset Make' />
</FormField>
<FormField label='AssetType' labelFor='asset_type'>
@ -161,6 +171,44 @@ const EditAssets = () => {
/>
</FormField>
<FormField label='PO Number'>
<Field name='asset_po' placeholder='PO Number' />
</FormField>
<FormField label='Asset Eol'>
<DatePicker
dateFormat='yyyy-MM-dd hh:mm'
showTimeSelect
selected={
initialValues.asset_eol
? new Date(
dayjs(initialValues.asset_eol).format(
'YYYY-MM-DD hh:mm',
),
)
: null
}
onChange={(date) =>
setInitialValues({ ...initialValues, asset_eol: date })
}
/>
</FormField>
<FormField label='Asset Purchase Price'>
<Field
name='asset_purchase_price'
placeholder='Asset Purchase Price'
/>
</FormField>
<FormField label='Asset Model'>
<Field name='asset_model' placeholder='Asset Model' />
</FormField>
<FormField label='Asset Tag'>
<Field name='asset_tag' placeholder='Asset Tag' />
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />

View File

@ -36,7 +36,7 @@ const EditAssetsPage = () => {
const router = useRouter();
const dispatch = useAppDispatch();
const initVals = {
asset_name: '',
asset_make: '',
asset_type: '',
@ -45,6 +45,16 @@ const EditAssetsPage = () => {
purchase_date: new Date(),
maintenance_due_date: new Date(),
asset_po: '',
asset_eol: new Date(),
asset_purchase_price: '',
asset_model: '',
asset_tag: '',
};
const [initialValues, setInitialValues] = useState(initVals);
@ -95,8 +105,8 @@ const EditAssetsPage = () => {
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField label='AssetName'>
<Field name='asset_name' placeholder='AssetName' />
<FormField label='Asset Make'>
<Field name='asset_make' placeholder='Asset Make' />
</FormField>
<FormField label='AssetType' labelFor='asset_type'>
@ -159,6 +169,44 @@ const EditAssetsPage = () => {
/>
</FormField>
<FormField label='PO Number'>
<Field name='asset_po' placeholder='PO Number' />
</FormField>
<FormField label='Asset Eol'>
<DatePicker
dateFormat='yyyy-MM-dd hh:mm'
showTimeSelect
selected={
initialValues.asset_eol
? new Date(
dayjs(initialValues.asset_eol).format(
'YYYY-MM-DD hh:mm',
),
)
: null
}
onChange={(date) =>
setInitialValues({ ...initialValues, asset_eol: date })
}
/>
</FormField>
<FormField label='Asset Purchase Price'>
<Field
name='asset_purchase_price'
placeholder='Asset Purchase Price'
/>
</FormField>
<FormField label='Asset Model'>
<Field name='asset_model' placeholder='Asset Model' />
</FormField>
<FormField label='Asset Tag'>
<Field name='asset_tag' placeholder='Asset Tag' />
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />

View File

@ -29,7 +29,11 @@ const AssetsTablesPage = () => {
const dispatch = useAppDispatch();
const [filters] = useState([
{ label: 'AssetName', title: 'asset_name' },
{ label: 'Asset Make', title: 'asset_make' },
{ label: 'PO Number', title: 'asset_po' },
{ label: 'Asset Purchase Price', title: 'asset_purchase_price' },
{ label: 'Asset Model', title: 'asset_model' },
{ label: 'Asset Tag', title: 'asset_tag' },
{ label: 'PurchaseDate', title: 'purchase_date', date: 'true' },
{
@ -37,6 +41,7 @@ const AssetsTablesPage = () => {
title: 'maintenance_due_date',
date: 'true',
},
{ label: 'Asset Eol', title: 'asset_eol', date: 'true' },
{ label: 'AssignedTo', title: 'assigned_to' },
@ -139,10 +144,6 @@ const AssetsTablesPage = () => {
<div className='md:inline-flex items-center ms-auto'>
<div id='delete-rows-button'></div>
</div>
<div className='md:inline-flex items-center ms-auto'>
<Link href={'/assets/assets-table'}>Switch to Table</Link>
</div>
</CardBox>
<CardBox className='mb-6' hasTable>

View File

@ -33,7 +33,7 @@ import { useRouter } from 'next/router';
import moment from 'moment';
const initialValues = {
asset_name: '',
asset_make: '',
asset_type: 'Hardware',
@ -42,15 +42,22 @@ const initialValues = {
purchase_date: '',
maintenance_due_date: '',
asset_po: '',
asset_eol: '',
asset_purchase_price: '',
asset_model: '',
asset_tag: '',
};
const AssetsNew = () => {
const router = useRouter();
const dispatch = useAppDispatch();
// get from url params
const { dateRangeStart, dateRangeEnd } = router.query;
const handleSubmit = async (data) => {
await dispatch(create(data));
await router.push('/assets/assets-list');
@ -70,22 +77,12 @@ const AssetsNew = () => {
</SectionTitleLineWithButton>
<CardBox>
<Formik
initialValues={
dateRangeStart && dateRangeEnd
? {
...initialValues,
purchase_date:
moment(dateRangeStart).format('YYYY-MM-DDTHH:mm'),
maintenance_due_date:
moment(dateRangeEnd).format('YYYY-MM-DDTHH:mm'),
}
: initialValues
}
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField label='AssetName'>
<Field name='asset_name' placeholder='AssetName' />
<FormField label='Asset Make'>
<Field name='asset_make' placeholder='Asset Make' />
</FormField>
<FormField label='AssetType' labelFor='asset_type'>
@ -122,6 +119,33 @@ const AssetsNew = () => {
/>
</FormField>
<FormField label='PO Number'>
<Field name='asset_po' placeholder='PO Number' />
</FormField>
<FormField label='Asset Eol'>
<Field
type='datetime-local'
name='asset_eol'
placeholder='Asset Eol'
/>
</FormField>
<FormField label='Asset Purchase Price'>
<Field
name='asset_purchase_price'
placeholder='Asset Purchase Price'
/>
</FormField>
<FormField label='Asset Model'>
<Field name='asset_model' placeholder='Asset Model' />
</FormField>
<FormField label='Asset Tag'>
<Field name='asset_tag' placeholder='Asset Tag' />
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />

View File

@ -29,7 +29,11 @@ const AssetsTablesPage = () => {
const dispatch = useAppDispatch();
const [filters] = useState([
{ label: 'AssetName', title: 'asset_name' },
{ label: 'Asset Make', title: 'asset_make' },
{ label: 'PO Number', title: 'asset_po' },
{ label: 'Asset Purchase Price', title: 'asset_purchase_price' },
{ label: 'Asset Model', title: 'asset_model' },
{ label: 'Asset Tag', title: 'asset_tag' },
{ label: 'PurchaseDate', title: 'purchase_date', date: 'true' },
{
@ -37,6 +41,7 @@ const AssetsTablesPage = () => {
title: 'maintenance_due_date',
date: 'true',
},
{ label: 'Asset Eol', title: 'asset_eol', date: 'true' },
{ label: 'AssignedTo', title: 'assigned_to' },
@ -140,7 +145,7 @@ const AssetsTablesPage = () => {
<div id='delete-rows-button'></div>
<Link href={'/assets/assets-list'}>
Back to <span className='capitalize'>calendar</span>
Back to <span className='capitalize'>table</span>
</Link>
</div>
</CardBox>

View File

@ -55,8 +55,8 @@ const AssetsView = () => {
</SectionTitleLineWithButton>
<CardBox>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>AssetName</p>
<p>{assets?.asset_name}</p>
<p className={'block font-bold mb-2'}>Asset Make</p>
<p>{assets?.asset_make}</p>
</div>
<div className={'mb-4'}>
@ -110,6 +110,45 @@ const AssetsView = () => {
)}
</FormField>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>PO Number</p>
<p>{assets?.asset_po}</p>
</div>
<FormField label='Asset Eol'>
{assets.asset_eol ? (
<DatePicker
dateFormat='yyyy-MM-dd hh:mm'
showTimeSelect
selected={
assets.asset_eol
? new Date(
dayjs(assets.asset_eol).format('YYYY-MM-DD hh:mm'),
)
: null
}
disabled
/>
) : (
<p>No Asset Eol</p>
)}
</FormField>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Asset Purchase Price</p>
<p>{assets?.asset_purchase_price}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Asset Model</p>
<p>{assets?.asset_model}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Asset Tag</p>
<p>{assets?.asset_tag}</p>
</div>
<BaseDivider />
<BaseButton

View File

@ -131,14 +131,6 @@ const Compliance_certificatesTablesPage = () => {
<div className='md:inline-flex items-center ms-auto'>
<div id='delete-rows-button'></div>
</div>
<div className='md:inline-flex items-center ms-auto'>
<Link
href={'/compliance_certificates/compliance_certificates-table'}
>
Switch to Table
</Link>
</div>
</CardBox>
<CardBox className='mb-6' hasTable>

View File

@ -46,9 +46,6 @@ const Compliance_certificatesNew = () => {
const router = useRouter();
const dispatch = useAppDispatch();
// get from url params
const { dateRangeStart, dateRangeEnd } = router.query;
const handleSubmit = async (data) => {
await dispatch(create(data));
await router.push('/compliance_certificates/compliance_certificates-list');
@ -68,17 +65,7 @@ const Compliance_certificatesNew = () => {
</SectionTitleLineWithButton>
<CardBox>
<Formik
initialValues={
dateRangeStart && dateRangeEnd
? {
...initialValues,
issue_date:
moment(dateRangeStart).format('YYYY-MM-DDTHH:mm'),
expiry_date:
moment(dateRangeEnd).format('YYYY-MM-DDTHH:mm'),
}
: initialValues
}
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
<Form>

View File

@ -134,7 +134,7 @@ const Compliance_certificatesTablesPage = () => {
<Link
href={'/compliance_certificates/compliance_certificates-list'}
>
Back to <span className='capitalize'>calendar</span>
Back to <span className='capitalize'>table</span>
</Link>
</div>
</CardBox>

View File

@ -0,0 +1,44 @@
import React, { ReactElement, useState } from 'react';
import Head from 'next/head';
import { mdiChartTimelineVariant } from '@mdi/js';
import { getPageTitle } from '../config';
import LayoutAuthenticated from '../layouts/Authenticated';
import SectionMain from '../components/SectionMain';
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
import CardBox from '../components/CardBox';
import TableEmployees from '../components/Employees/TableEmployees';
const EmployeesStatusReport = () ={
const [filters] = useState([
{ label: 'Status', title: 'status', type: 'enum', options: ['Active', 'Inactive'] },
]);
const [filterItems, setFilterItems] = useState([]);
return (
<>
<Head>
<title>{getPageTitle('Employees (Active/Inactive)')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title="Employees (Active/Inactive)"
/>
<CardBox className="mb-6" hasTable>
<TableEmployees
filterItems={filterItems}
setFilterItems={setFilterItems}
filters={filters}
showGrid={false}
/>
</CardBox>
</SectionMain>
</>
);
};
EmployeesStatusReport.getLayout = (page: ReactElement) => (
<LayoutAuthenticated permission="READ_EMPLOYEES">{page}</LayoutAuthenticated>
);
export default EmployeesStatusReport;

View File

@ -96,13 +96,23 @@ const EmployeesView = () => {
<table>
<thead>
<tr>
<th>AssetName</th>
<th>Asset Make</th>
<th>AssetType</th>
<th>PurchaseDate</th>
<th>MaintenanceDueDate</th>
<th>PO Number</th>
<th>Asset Eol</th>
<th>Asset Purchase Price</th>
<th>Asset Model</th>
<th>Asset Tag</th>
</tr>
</thead>
<tbody>
@ -115,7 +125,7 @@ const EmployeesView = () => {
router.push(`/assets/assets-view/?id=${item.id}`)
}
>
<td data-label='asset_name'>{item.asset_name}</td>
<td data-label='asset_make'>{item.asset_make}</td>
<td data-label='asset_type'>{item.asset_type}</td>
@ -130,6 +140,20 @@ const EmployeesView = () => {
item.maintenance_due_date,
)}
</td>
<td data-label='asset_po'>{item.asset_po}</td>
<td data-label='asset_eol'>
{dataFormatter.dateTimeFormatter(item.asset_eol)}
</td>
<td data-label='asset_purchase_price'>
{item.asset_purchase_price}
</td>
<td data-label='asset_model'>{item.asset_model}</td>
<td data-label='asset_tag'>{item.asset_tag}</td>
</tr>
))}
</tbody>

View File

@ -93,7 +93,7 @@ export default function WebSite() {
<FeaturesSection
projectName={'Nextry'}
image={['Efficient management tools display']}
withBg={1}
withBg={0}
features={features_points}
mainText={`Discover Key Features of ${projectName}`}
subTitle={`Unlock the full potential of your workforce and assets with ${projectName}. Explore our powerful features designed to enhance efficiency and security.`}

View File

@ -77,7 +77,7 @@ export default function WebSite() {
<FeaturesSection
projectName={'Nextry'}
image={['Efficient management tools display']}
withBg={0}
withBg={1}
features={features_points}
mainText={`Discover Key Features of ${projectName}`}
subTitle={`Unlock the full potential of your workforce and assets with ${projectName}. Explore our powerful features designed to enhance efficiency and security.`}