Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -36,8 +36,6 @@ const Departments = db.departments;
|
||||
|
||||
const RolePermissions = db.role_permissions;
|
||||
|
||||
const Permissions = db.permissions;
|
||||
|
||||
const ApprovalWorkflows = db.approval_workflows;
|
||||
|
||||
const ApprovalSteps = db.approval_steps;
|
||||
|
||||
@ -1,162 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const bcrypt = require('bcrypt');
|
||||
const { QueryTypes } = require('sequelize');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const config = require('../../config');
|
||||
|
||||
const demoAccounts = [
|
||||
{
|
||||
email: 'super_admin@flatlogic.com',
|
||||
password: config.admin_pass,
|
||||
roleName: 'Super Administrator',
|
||||
firstName: 'Super',
|
||||
lastName: 'Administrator',
|
||||
},
|
||||
{
|
||||
email: 'admin@flatlogic.com',
|
||||
password: config.admin_pass,
|
||||
roleName: 'Administrator',
|
||||
firstName: 'Organization',
|
||||
lastName: 'Administrator',
|
||||
},
|
||||
{
|
||||
email: 'director.general@flatlogic.com',
|
||||
previousEmails: ['client@hello.com'],
|
||||
password: config.user_pass,
|
||||
roleName: 'Director General',
|
||||
firstName: 'Director',
|
||||
lastName: 'General',
|
||||
},
|
||||
{
|
||||
email: 'finance.director@flatlogic.com',
|
||||
previousEmails: ['john@doe.com'],
|
||||
password: config.user_pass,
|
||||
roleName: 'Finance Director',
|
||||
firstName: 'Finance',
|
||||
lastName: 'Director',
|
||||
},
|
||||
{
|
||||
email: 'procurement.lead@flatlogic.com',
|
||||
password: config.user_pass,
|
||||
roleName: 'Procurement Lead',
|
||||
firstName: 'Procurement',
|
||||
lastName: 'Lead',
|
||||
},
|
||||
{
|
||||
email: 'compliance.audit@flatlogic.com',
|
||||
password: config.user_pass,
|
||||
roleName: 'Compliance and Audit Lead',
|
||||
firstName: 'Compliance',
|
||||
lastName: 'Audit Lead',
|
||||
},
|
||||
{
|
||||
email: 'project.delivery@flatlogic.com',
|
||||
password: config.user_pass,
|
||||
roleName: 'Project Delivery Lead',
|
||||
firstName: 'Project',
|
||||
lastName: 'Delivery Lead',
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
|
||||
try {
|
||||
for (const account of demoAccounts) {
|
||||
const role = await queryInterface.sequelize.query(
|
||||
'SELECT id FROM "roles" WHERE name = :roleName LIMIT 1',
|
||||
{
|
||||
replacements: { roleName: account.roleName },
|
||||
type: QueryTypes.SELECT,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
if (!role[0]?.id) {
|
||||
throw new Error(`Role not found for demo account: ${account.roleName}`);
|
||||
}
|
||||
|
||||
let existingUser = null;
|
||||
|
||||
for (const candidateEmail of [account.email, ...(account.previousEmails || [])]) {
|
||||
const user = await queryInterface.sequelize.query(
|
||||
'SELECT id, email FROM "users" WHERE email = :email LIMIT 1',
|
||||
{
|
||||
replacements: { email: candidateEmail },
|
||||
type: QueryTypes.SELECT,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
if (user[0]?.id) {
|
||||
existingUser = user[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const passwordHash = bcrypt.hashSync(account.password, config.bcrypt.saltRounds);
|
||||
const now = new Date();
|
||||
|
||||
if (existingUser?.id) {
|
||||
await queryInterface.sequelize.query(
|
||||
`UPDATE "users"
|
||||
SET "firstName" = :firstName,
|
||||
"lastName" = :lastName,
|
||||
"email" = :email,
|
||||
"password" = :password,
|
||||
"emailVerified" = true,
|
||||
"provider" = :provider,
|
||||
"disabled" = false,
|
||||
"app_roleId" = :roleId,
|
||||
"deletedAt" = NULL,
|
||||
"updatedAt" = :updatedAt
|
||||
WHERE id = :id`,
|
||||
{
|
||||
replacements: {
|
||||
id: existingUser.id,
|
||||
firstName: account.firstName,
|
||||
lastName: account.lastName,
|
||||
email: account.email,
|
||||
password: passwordHash,
|
||||
provider: config.providers.LOCAL,
|
||||
roleId: role[0].id,
|
||||
updatedAt: now,
|
||||
},
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await queryInterface.bulkInsert(
|
||||
'users',
|
||||
[
|
||||
{
|
||||
id: uuidv4(),
|
||||
firstName: account.firstName,
|
||||
lastName: account.lastName,
|
||||
email: account.email,
|
||||
emailVerified: true,
|
||||
provider: config.providers.LOCAL,
|
||||
password: passwordHash,
|
||||
disabled: false,
|
||||
app_roleId: role[0].id,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
],
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('Error upserting role demo users:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async down() {},
|
||||
};
|
||||
@ -1,114 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { QueryTypes } = require('sequelize');
|
||||
|
||||
const rolePermissionMap = {
|
||||
'Director General': [
|
||||
'READ_APPROVALS',
|
||||
'READ_PROJECTS',
|
||||
'READ_CONTRACTS',
|
||||
'READ_NOTIFICATIONS',
|
||||
],
|
||||
'Finance Director': [
|
||||
'READ_PAYMENT_REQUESTS',
|
||||
'READ_ALLOCATIONS',
|
||||
'READ_CONTRACTS',
|
||||
'READ_APPROVALS',
|
||||
'READ_NOTIFICATIONS',
|
||||
],
|
||||
'Procurement Lead': [
|
||||
'READ_REQUISITIONS',
|
||||
'READ_TENDERS',
|
||||
'READ_VENDORS',
|
||||
'READ_VENDOR_COMPLIANCE_DOCUMENTS',
|
||||
'READ_BIDS',
|
||||
'READ_BID_EVALUATIONS',
|
||||
'READ_AWARDS',
|
||||
'READ_CONTRACTS',
|
||||
'READ_APPROVALS',
|
||||
'READ_NOTIFICATIONS',
|
||||
],
|
||||
'Compliance and Audit Lead': [
|
||||
'READ_COMPLIANCE_ALERTS',
|
||||
'READ_AUDIT_LOGS',
|
||||
'READ_DOCUMENTS',
|
||||
'READ_APPROVALS',
|
||||
'READ_CONTRACTS',
|
||||
'READ_NOTIFICATIONS',
|
||||
],
|
||||
'Project Delivery Lead': [
|
||||
'READ_PROJECTS',
|
||||
'READ_PROJECT_MILESTONES',
|
||||
'READ_RISKS',
|
||||
'READ_ISSUES',
|
||||
'READ_FIELD_VERIFICATIONS',
|
||||
'READ_PROGRAMS',
|
||||
'READ_PROVINCES',
|
||||
'READ_CONTRACTS',
|
||||
'READ_APPROVALS',
|
||||
'READ_PAYMENT_REQUESTS',
|
||||
'READ_NOTIFICATIONS',
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
const now = new Date();
|
||||
|
||||
try {
|
||||
for (const [roleName, permissionNames] of Object.entries(rolePermissionMap)) {
|
||||
const roles = await queryInterface.sequelize.query(
|
||||
'SELECT id FROM "roles" WHERE name = :roleName LIMIT 1',
|
||||
{
|
||||
replacements: { roleName },
|
||||
type: QueryTypes.SELECT,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
if (!roles[0]?.id) {
|
||||
throw new Error(`Role not found while assigning workspace permissions: ${roleName}`);
|
||||
}
|
||||
|
||||
for (const permissionName of permissionNames) {
|
||||
const permissions = await queryInterface.sequelize.query(
|
||||
'SELECT id FROM "permissions" WHERE name = :permissionName LIMIT 1',
|
||||
{
|
||||
replacements: { permissionName },
|
||||
type: QueryTypes.SELECT,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
if (!permissions[0]?.id) {
|
||||
throw new Error(`Permission not found while assigning workspace permissions: ${permissionName}`);
|
||||
}
|
||||
|
||||
await queryInterface.sequelize.query(
|
||||
`INSERT INTO "rolesPermissionsPermissions" ("createdAt", "updatedAt", "roles_permissionsId", "permissionId")
|
||||
VALUES (:createdAt, :updatedAt, :roleId, :permissionId)
|
||||
ON CONFLICT ("roles_permissionsId", "permissionId") DO NOTHING`,
|
||||
{
|
||||
replacements: {
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
roleId: roles[0].id,
|
||||
permissionId: permissions[0].id,
|
||||
},
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('Error assigning workspace read permissions:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async down() {},
|
||||
};
|
||||
@ -1,373 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const nameMap = {
|
||||
'Grace Hopper': 'Kinshasa Capital Delivery Office',
|
||||
'Alan Turing': 'Eastern Corridor Operations Unit',
|
||||
'Ada Lovelace': 'National Public Investment Office',
|
||||
'Marie Curie': 'Provincial Services Coordination Office',
|
||||
};
|
||||
|
||||
const provinceNameMap = {
|
||||
'Grace Hopper': 'Kinshasa',
|
||||
'Alan Turing': 'North Kivu',
|
||||
'Ada Lovelace': 'Haut-Katanga',
|
||||
'Marie Curie': 'Kasaï Central',
|
||||
};
|
||||
|
||||
const provinceCodeMap = {
|
||||
'Grace Hopper': 'KIN',
|
||||
'Alan Turing': 'NKV',
|
||||
'Ada Lovelace': 'HKT',
|
||||
'Marie Curie': 'KSC',
|
||||
};
|
||||
|
||||
const departmentNameMap = {
|
||||
'Grace Hopper': 'Procurement Directorate',
|
||||
'Alan Turing': 'Finance Directorate',
|
||||
'Ada Lovelace': 'Project Delivery Unit',
|
||||
'Marie Curie': 'Internal Audit Office',
|
||||
};
|
||||
|
||||
const departmentCodeMap = {
|
||||
'Grace Hopper': 'PD',
|
||||
'Alan Turing': 'FD',
|
||||
'Ada Lovelace': 'PDU',
|
||||
'Marie Curie': 'IAO',
|
||||
};
|
||||
|
||||
const programmeNameMap = {
|
||||
'Grace Hopper': 'Road Connectivity Programme',
|
||||
'Alan Turing': 'Water Access Programme',
|
||||
'Ada Lovelace': 'School Rehabilitation Programme',
|
||||
'Marie Curie': 'Primary Health Strengthening Programme',
|
||||
};
|
||||
|
||||
const programmeCodeMap = {
|
||||
'Grace Hopper': 'RCP',
|
||||
'Alan Turing': 'WAP',
|
||||
'Ada Lovelace': 'SRP',
|
||||
'Marie Curie': 'PHSP',
|
||||
};
|
||||
|
||||
const projectNameMap = {
|
||||
'Grace Hopper': 'Bukavu Water Network Upgrade',
|
||||
'Alan Turing': 'Goma School Rehabilitation Phase 1',
|
||||
'Ada Lovelace': 'Kananga Rural Roads Package A',
|
||||
'Marie Curie': 'Lubumbashi Health Facilities Modernization',
|
||||
};
|
||||
|
||||
const projectCodeMap = {
|
||||
'Grace Hopper': 'BWU',
|
||||
'Alan Turing': 'GSR',
|
||||
'Ada Lovelace': 'KRR',
|
||||
'Marie Curie': 'LHF',
|
||||
};
|
||||
|
||||
const vendorNameMap = {
|
||||
'Grace Hopper': 'Congo Build Partners',
|
||||
'Alan Turing': 'Great Lakes Engineering',
|
||||
'Ada Lovelace': 'Equator Supply Services',
|
||||
'Marie Curie': 'Civic Works Consortium',
|
||||
};
|
||||
|
||||
const contractTitleMap = {
|
||||
'Grace Hopper': 'Road works package',
|
||||
'Alan Turing': 'Medical equipment supply',
|
||||
'Ada Lovelace': 'School rehabilitation lot',
|
||||
'Marie Curie': 'Water network expansion',
|
||||
};
|
||||
|
||||
const workflowNameMap = {
|
||||
'Grace Hopper': 'Capital Project Approval',
|
||||
'Alan Turing': 'Vendor Compliance Review',
|
||||
'Ada Lovelace': 'Budget Reallocation Approval',
|
||||
'Marie Curie': 'Payment Authorization Workflow',
|
||||
};
|
||||
|
||||
const stepNameMap = {
|
||||
'Grace Hopper': 'Initial Review',
|
||||
'Alan Turing': 'Department Approval',
|
||||
'Ada Lovelace': 'Finance Clearance',
|
||||
'Marie Curie': 'Executive Sign-off',
|
||||
};
|
||||
|
||||
const recordTypeMap = {
|
||||
'Grace Hopper': 'projects',
|
||||
'Alan Turing': 'contracts',
|
||||
'Ada Lovelace': 'payment_requests',
|
||||
'Marie Curie': 'requisitions',
|
||||
};
|
||||
|
||||
const referencePrefixMap = {
|
||||
'Grace Hopper': 'REF-OPS',
|
||||
'Alan Turing': 'REF-CTL',
|
||||
'Ada Lovelace': 'REF-DEL',
|
||||
'Marie Curie': 'REF-CMP',
|
||||
};
|
||||
|
||||
const notificationTitleMap = {
|
||||
'Grace Hopper': 'Contract action required',
|
||||
'Alan Turing': 'Project update available',
|
||||
'Ada Lovelace': 'Approval decision pending',
|
||||
'Marie Curie': 'Compliance evidence due',
|
||||
};
|
||||
|
||||
const narrativeMap = {
|
||||
'Grace Hopper': 'Priority follow-up is required on this record.',
|
||||
'Alan Turing': 'Operational review is in progress for this item.',
|
||||
'Ada Lovelace': 'Programme delivery details are under active review.',
|
||||
'Marie Curie': 'Supporting evidence and control checks are pending.',
|
||||
};
|
||||
|
||||
const alertTitleMap = {
|
||||
'Grace Hopper': 'Contract monitoring alert',
|
||||
'Alan Turing': 'Project delivery alert',
|
||||
'Ada Lovelace': 'Payment control alert',
|
||||
'Marie Curie': 'Compliance evidence alert',
|
||||
};
|
||||
|
||||
const escapeValue = (queryInterface, value) => queryInterface.sequelize.escape(value);
|
||||
|
||||
const updateExactValues = async (queryInterface, transaction, table, column, valueMap) => {
|
||||
const entries = Object.entries(valueMap);
|
||||
|
||||
if (!entries.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const whenClauses = entries
|
||||
.map(([from, to]) => `WHEN ${escapeValue(queryInterface, from)} THEN ${escapeValue(queryInterface, to)}`)
|
||||
.join(' ');
|
||||
|
||||
const fromList = entries.map(([from]) => escapeValue(queryInterface, from)).join(', ');
|
||||
|
||||
await queryInterface.sequelize.query(
|
||||
`UPDATE "${table}"
|
||||
SET "${column}" = CASE "${column}" ${whenClauses} ELSE "${column}" END
|
||||
WHERE "${column}" IN (${fromList})`,
|
||||
{ transaction },
|
||||
);
|
||||
};
|
||||
|
||||
const updateWithIdSuffix = async (queryInterface, transaction, table, column, valueMap, separator = ' ') => {
|
||||
const entries = Object.entries(valueMap);
|
||||
|
||||
if (!entries.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const whenClauses = entries
|
||||
.map(([from, to]) => {
|
||||
const base = escapeValue(queryInterface, to);
|
||||
const sep = escapeValue(queryInterface, separator);
|
||||
return `WHEN ${escapeValue(queryInterface, from)} THEN CONCAT(${base}, ${sep}, RIGHT(REPLACE(id::text, '-', ''), 4))`;
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
const fromList = entries.map(([from]) => escapeValue(queryInterface, from)).join(', ');
|
||||
|
||||
await queryInterface.sequelize.query(
|
||||
`UPDATE "${table}"
|
||||
SET "${column}" = CASE "${column}" ${whenClauses} ELSE "${column}" END
|
||||
WHERE "${column}" IN (${fromList})`,
|
||||
{ transaction },
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'organizations', 'name', nameMap);
|
||||
await updateExactValues(queryInterface, transaction, 'provinces', 'name', provinceNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'provinces', 'code', provinceCodeMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'departments', 'name', departmentNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'departments', 'code', departmentCodeMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'approval_workflows', 'name', workflowNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'approval_steps', 'name', stepNameMap);
|
||||
await updateExactValues(queryInterface, transaction, 'approvals', 'record_type', recordTypeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'notifications', 'title', notificationTitleMap);
|
||||
await updateExactValues(queryInterface, transaction, 'notifications', 'message', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'notifications', 'record_type', recordTypeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'fiscal_years', 'name', {
|
||||
'Grace Hopper': 'FY 2024/25',
|
||||
'Alan Turing': 'FY 2025/26',
|
||||
'Ada Lovelace': 'FY 2026/27',
|
||||
'Marie Curie': 'FY 2027/28',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'funding_sources', 'name', {
|
||||
'Grace Hopper': 'Treasury Capital Grant',
|
||||
'Alan Turing': 'Provincial Development Transfer',
|
||||
'Ada Lovelace': 'Infrastructure Recovery Fund',
|
||||
'Marie Curie': 'Service Delivery Support Fund',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'funding_sources', 'reference_code', referencePrefixMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'budget_programs', 'name', programmeNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'budget_programs', 'code', programmeCodeMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'budget_lines', 'name', {
|
||||
'Grace Hopper': 'Civil Works Envelope',
|
||||
'Alan Turing': 'Equipment and Materials',
|
||||
'Ada Lovelace': 'Professional Services',
|
||||
'Marie Curie': 'Monitoring and Evaluation',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'budget_lines', 'code', {
|
||||
'Grace Hopper': 'BL-CW',
|
||||
'Alan Turing': 'BL-EQ',
|
||||
'Ada Lovelace': 'BL-PS',
|
||||
'Marie Curie': 'BL-ME',
|
||||
}, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'budget_lines', 'description', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'procurement_plans', 'name', {
|
||||
'Grace Hopper': 'Annual Infrastructure Sourcing Plan',
|
||||
'Alan Turing': 'Education Recovery Procurement Plan',
|
||||
'Ada Lovelace': 'Provincial Delivery Procurement Plan',
|
||||
'Marie Curie': 'Health Services Procurement Plan',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'requisitions', 'requisition_number', referencePrefixMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'requisitions', 'title', {
|
||||
'Grace Hopper': 'Road maintenance works request',
|
||||
'Alan Turing': 'Laboratory equipment request',
|
||||
'Ada Lovelace': 'School repair materials request',
|
||||
'Marie Curie': 'Water network extension request',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'requisitions', 'scope_of_work', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'tenders', 'tender_number', referencePrefixMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'tenders', 'title', {
|
||||
'Grace Hopper': 'Open tender for civil works',
|
||||
'Alan Turing': 'Restricted tender for equipment supply',
|
||||
'Ada Lovelace': 'Framework tender for school rehabilitation',
|
||||
'Marie Curie': 'Competitive tender for water services expansion',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'tenders', 'eligibility_criteria', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'tenders', 'submission_instructions', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'vendors', 'name', vendorNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'vendors', 'trade_name', vendorNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'vendors', 'contact_name', {
|
||||
'Grace Hopper': 'Patrick Ilunga',
|
||||
'Alan Turing': 'Aline Mbuyi',
|
||||
'Ada Lovelace': 'David Kasongo',
|
||||
'Marie Curie': 'Ruth Mukendi',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'vendors', 'address', {
|
||||
'Grace Hopper': 'Kinshasa headquarters office',
|
||||
'Alan Turing': 'Goma regional office',
|
||||
'Ada Lovelace': 'Kananga delivery office',
|
||||
'Marie Curie': 'Lubumbashi programme office',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'vendor_compliance_documents', 'reference_number', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'vendor_compliance_documents', 'review_comment', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'bids', 'bid_reference', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'bids', 'notes', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'bid_evaluations', 'justification', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'awards', 'award_number', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'awards', 'award_memo', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'programs', 'name', programmeNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'programs', 'code', programmeCodeMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'projects', 'name', projectNameMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'projects', 'project_code', projectCodeMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'projects', 'implementing_entity', {
|
||||
'Grace Hopper': 'Directorate of Public Works',
|
||||
'Alan Turing': 'Directorate of Basic Education',
|
||||
'Ada Lovelace': 'Provincial Infrastructure Unit',
|
||||
'Marie Curie': 'Directorate of Health Services',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'projects', 'objectives', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'project_milestones', 'name', {
|
||||
'Grace Hopper': 'Mobilization complete',
|
||||
'Alan Turing': 'Site preparation complete',
|
||||
'Ada Lovelace': 'Materials delivered',
|
||||
'Marie Curie': 'Final inspection complete',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'project_milestones', 'description', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'risks', 'title', {
|
||||
'Grace Hopper': 'Contract schedule slippage risk',
|
||||
'Alan Turing': 'Funding delay risk',
|
||||
'Ada Lovelace': 'Approval bottleneck risk',
|
||||
'Marie Curie': 'Evidence gap risk',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'risks', 'description', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'risks', 'mitigation_plan', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'issues', 'title', {
|
||||
'Grace Hopper': 'Procurement handoff issue',
|
||||
'Alan Turing': 'Payment processing issue',
|
||||
'Ada Lovelace': 'Delivery coordination issue',
|
||||
'Marie Curie': 'Compliance follow-up issue',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'issues', 'description', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'field_verifications', 'findings', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'field_verifications', 'actions_required', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'contracts', 'title', contractTitleMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'contracts', 'contract_number', {
|
||||
'Grace Hopper': 'CT-RD',
|
||||
'Alan Turing': 'CT-ME',
|
||||
'Ada Lovelace': 'CT-SR',
|
||||
'Marie Curie': 'CT-WN',
|
||||
}, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'grants', 'funding_window_name', {
|
||||
'Grace Hopper': 'Community Infrastructure Grant Window',
|
||||
'Alan Turing': 'Education Access Grant Window',
|
||||
'Ada Lovelace': 'Provincial Services Grant Window',
|
||||
'Marie Curie': 'Health Systems Grant Window',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'grants', 'call_reference', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'grants', 'eligibility_rules', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'grants', 'notes', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'beneficiaries', 'name', {
|
||||
'Grace Hopper': 'Bukavu Community Water Board',
|
||||
'Alan Turing': 'Goma Education Improvement Council',
|
||||
'Ada Lovelace': 'Kananga Roads Maintenance Group',
|
||||
'Marie Curie': 'Lubumbashi Health Access Network',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'beneficiaries', 'registration_number', referencePrefixMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'grant_applications', 'application_reference', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'grant_applications', 'proposal_summary', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'grant_evaluations', 'comments', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'grant_tranches', 'conditions', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'expense_categories', 'name', {
|
||||
'Grace Hopper': 'Civil works expense',
|
||||
'Alan Turing': 'Equipment purchase expense',
|
||||
'Ada Lovelace': 'Consultancy services expense',
|
||||
'Marie Curie': 'Monitoring activity expense',
|
||||
});
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'expense_categories', 'code', {
|
||||
'Grace Hopper': 'EXP-CW',
|
||||
'Alan Turing': 'EXP-EQ',
|
||||
'Ada Lovelace': 'EXP-CS',
|
||||
'Marie Curie': 'EXP-ME',
|
||||
}, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'expense_categories', 'description', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'invoices', 'invoice_number', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'invoices', 'notes', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'payment_requests', 'request_number', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'payment_requests', 'justification', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'payment_batches', 'batch_number', referencePrefixMap, '-');
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'payments', 'payment_reference', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'payments', 'failure_reason', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'obligations', 'obligation_number', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'obligations', 'notes', narrativeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'ledger_entries', 'entry_reference', referencePrefixMap, '-');
|
||||
await updateExactValues(queryInterface, transaction, 'ledger_entries', 'description', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'ledger_entries', 'record_type', recordTypeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'documents', 'title', {
|
||||
'Grace Hopper': 'Signed contract dossier',
|
||||
'Alan Turing': 'Project monitoring report',
|
||||
'Ada Lovelace': 'Approval support memo',
|
||||
'Marie Curie': 'Compliance evidence file',
|
||||
});
|
||||
await updateExactValues(queryInterface, transaction, 'documents', 'description', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'documents', 'record_type', recordTypeMap);
|
||||
await updateWithIdSuffix(queryInterface, transaction, 'compliance_alerts', 'title', alertTitleMap);
|
||||
await updateExactValues(queryInterface, transaction, 'compliance_alerts', 'details', narrativeMap);
|
||||
await updateExactValues(queryInterface, transaction, 'compliance_alerts', 'record_type', recordTypeMap);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('Error cleaning sample business data:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async down() {},
|
||||
};
|
||||
@ -121,8 +121,6 @@ const documentsRoutes = require('./routes/documents');
|
||||
|
||||
const compliance_alertsRoutes = require('./routes/compliance_alerts');
|
||||
|
||||
const executive_summaryRoutes = require('./routes/executive_summary');
|
||||
|
||||
|
||||
const getBaseUrl = (url) => {
|
||||
if (!url) return '';
|
||||
@ -279,8 +277,6 @@ app.use('/api/documents', passport.authenticate('jwt', {session: false}), docume
|
||||
|
||||
app.use('/api/compliance_alerts', passport.authenticate('jwt', {session: false}), compliance_alertsRoutes);
|
||||
|
||||
app.use('/api/executive-summary', passport.authenticate('jwt', {session: false}), executive_summaryRoutes);
|
||||
|
||||
app.use(
|
||||
'/api/openai',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
|
||||
@ -22,11 +22,11 @@ const router = express.Router();
|
||||
* email:
|
||||
* type: string
|
||||
* default: admin@flatlogic.com
|
||||
* description: User email. Additional role demo accounts are listed on the login page.
|
||||
* description: User email
|
||||
* password:
|
||||
* type: string
|
||||
* default: 5e8f2960
|
||||
* description: User password. Additional role demo passwords are listed on the login page.
|
||||
* default: password
|
||||
* description: User password
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ export default function AsideMenu({
|
||||
<>
|
||||
<AsideMenuLayer
|
||||
menu={props.menu}
|
||||
className={`${isAsideMobileExpanded ? 'left-0' : '-left-72 lg:left-0'} ${
|
||||
className={`${isAsideMobileExpanded ? 'left-0' : '-left-60 lg:left-0'} ${
|
||||
!isAsideLgActive ? 'lg:hidden xl:flex' : ''
|
||||
}`}
|
||||
onAsideLgCloseClick={props.onAsideLgClose}
|
||||
|
||||
@ -1,32 +1,18 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { mdiChevronDown, mdiChevronUp } from '@mdi/js'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { mdiMinus, mdiPlus } from '@mdi/js'
|
||||
import BaseIcon from './BaseIcon'
|
||||
import Link from 'next/link'
|
||||
import { getButtonColor } from '../colors'
|
||||
import AsideMenuList from './AsideMenuList'
|
||||
import { MenuAsideItem } from '../interfaces'
|
||||
import { useAppSelector } from '../stores/hooks'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
type Props = {
|
||||
item: MenuAsideItem
|
||||
isDropdownList?: boolean
|
||||
}
|
||||
|
||||
const getActiveView = (path: string) => new URL(path, location.href).pathname.split('/')[1]
|
||||
|
||||
const itemMatchesActiveView = (menuItem: MenuAsideItem, activeView: string): boolean => {
|
||||
if (menuItem.href && getActiveView(menuItem.href) === activeView) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!menuItem.menu?.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
return menuItem.menu.some((nestedItem) => itemMatchesActiveView(nestedItem, activeView))
|
||||
}
|
||||
|
||||
const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
|
||||
const [isLinkActive, setIsLinkActive] = useState(false)
|
||||
const [isDropdownActive, setIsDropdownActive] = useState(false)
|
||||
@ -34,78 +20,77 @@ const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
|
||||
const asideMenuItemStyle = useAppSelector((state) => state.style.asideMenuItemStyle)
|
||||
const asideMenuDropdownStyle = useAppSelector((state) => state.style.asideMenuDropdownStyle)
|
||||
const asideMenuItemActiveStyle = useAppSelector((state) => state.style.asideMenuItemActiveStyle)
|
||||
const borders = useAppSelector((state) => state.style.borders)
|
||||
const activeLinkColor = useAppSelector((state) => state.style.activeLinkColor)
|
||||
const borders = useAppSelector((state) => state.style.borders);
|
||||
const activeLinkColor = useAppSelector(
|
||||
(state) => state.style.activeLinkColor,
|
||||
);
|
||||
const activeClassAddon = !item.color && isLinkActive ? asideMenuItemActiveStyle : ''
|
||||
const isGroupTrigger = Boolean(item.menu && !item.href && !isDropdownList)
|
||||
|
||||
const { asPath, isReady } = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReady) {
|
||||
return
|
||||
}
|
||||
if (item.href && isReady) {
|
||||
const linkPathName = new URL(item.href, location.href).pathname + '/';
|
||||
const activePathname = new URL(asPath, location.href).pathname
|
||||
|
||||
const activeView = getActiveView(asPath)
|
||||
const activeView = activePathname.split('/')[1];
|
||||
const linkPathNameView = linkPathName.split('/')[1];
|
||||
|
||||
if (item.href) {
|
||||
setIsLinkActive(getActiveView(item.href) === activeView)
|
||||
setIsLinkActive(linkPathNameView === activeView);
|
||||
}
|
||||
|
||||
if (item.menu?.length) {
|
||||
setIsDropdownActive(item.menu.some((nestedItem) => itemMatchesActiveView(nestedItem, activeView)))
|
||||
}
|
||||
}, [item.href, item.menu, isReady, asPath])
|
||||
}, [item.href, isReady, asPath])
|
||||
|
||||
const asideMenuItemInnerContents = (
|
||||
<>
|
||||
{item.icon && (
|
||||
<span className='flex h-9 w-9 flex-none items-center justify-center rounded-xl'>
|
||||
<BaseIcon path={item.icon} className={`${activeClassAddon}`} size='18' />
|
||||
</span>
|
||||
<BaseIcon path={item.icon} className={`flex-none mx-3 ${activeClassAddon}`} size="18" />
|
||||
)}
|
||||
<span
|
||||
className={`min-w-0 grow whitespace-normal break-words leading-5 line-clamp-2 ${item.menu ? '' : 'pr-3'} ${isGroupTrigger ? 'text-[0.93rem] tracking-[0.01em]' : ''} ${activeClassAddon}`}
|
||||
className={`grow text-ellipsis line-clamp-1 ${
|
||||
item.menu ? '' : 'pr-12'
|
||||
} ${activeClassAddon}`}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
{item.menu && (
|
||||
<BaseIcon
|
||||
path={isDropdownActive ? mdiChevronUp : mdiChevronDown}
|
||||
path={isDropdownActive ? mdiMinus : mdiPlus}
|
||||
className={`flex-none ${activeClassAddon}`}
|
||||
size='18'
|
||||
w="w-12"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
const componentClass = [
|
||||
'group flex w-full items-start gap-3 rounded-xl px-3 text-left transition-all duration-150',
|
||||
isDropdownList ? 'py-2 text-sm' : 'py-2.5',
|
||||
item.color ? getButtonColor(item.color, false, true) : `${asideMenuItemStyle}`,
|
||||
isGroupTrigger ? 'font-semibold' : '',
|
||||
isLinkActive ? `${activeLinkColor} shadow-sm` : '',
|
||||
].join(' ')
|
||||
'flex cursor-pointer py-1.5 ',
|
||||
isDropdownList ? 'px-6 text-sm' : '',
|
||||
item.color
|
||||
? getButtonColor(item.color, false, true)
|
||||
: `${asideMenuItemStyle}`,
|
||||
isLinkActive
|
||||
? `text-black ${activeLinkColor} dark:text-white dark:bg-dark-800`
|
||||
: '',
|
||||
].join(' ');
|
||||
|
||||
return (
|
||||
<li className={isDropdownList ? 'px-2 py-1' : 'px-2 py-1.5'}>
|
||||
<li className={'px-3 py-1.5'}>
|
||||
{item.withDevider && <hr className={`${borders} mb-3`} />}
|
||||
{item.href ? (
|
||||
{item.href && (
|
||||
<Link href={item.href} target={item.target} className={componentClass}>
|
||||
{asideMenuItemInnerContents}
|
||||
</Link>
|
||||
) : (
|
||||
<button type='button' className={componentClass} onClick={() => setIsDropdownActive(!isDropdownActive)}>
|
||||
)}
|
||||
{!item.href && (
|
||||
<div className={componentClass} onClick={() => setIsDropdownActive(!isDropdownActive)}>
|
||||
{asideMenuItemInnerContents}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{item.menu && (
|
||||
<AsideMenuList
|
||||
menu={item.menu}
|
||||
className={`${asideMenuDropdownStyle} ${
|
||||
isDropdownActive
|
||||
? 'mt-2 ml-3 block rounded-xl p-2'
|
||||
: 'hidden'
|
||||
isDropdownActive ? 'block dark:bg-slate-800/50' : 'hidden'
|
||||
}`}
|
||||
isDropdownList
|
||||
/>
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import axios from 'axios'
|
||||
import { mdiArrowTopRight, mdiClose, mdiOfficeBuildingOutline } from '@mdi/js'
|
||||
import { mdiLogout, mdiClose } from '@mdi/js'
|
||||
import BaseIcon from './BaseIcon'
|
||||
import AsideMenuList from './AsideMenuList'
|
||||
import { MenuAsideItem } from '../interfaces'
|
||||
import { useAppSelector } from '../stores/hooks'
|
||||
import { getWorkspaceConfig, getWorkspaceRoute } from '../helpers/workspace'
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useAppDispatch } from '../stores/hooks';
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import axios from 'axios';
|
||||
|
||||
|
||||
type Props = {
|
||||
menu: MenuAsideItem[]
|
||||
@ -14,102 +17,75 @@ type Props = {
|
||||
onAsideLgCloseClick: () => void
|
||||
}
|
||||
|
||||
type OrganizationOption = {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function AsideMenuLayer({ menu, className = '', ...props }: Props) {
|
||||
const corners = useAppSelector((state) => state.style.corners)
|
||||
const corners = useAppSelector((state) => state.style.corners);
|
||||
const asideStyle = useAppSelector((state) => state.style.asideStyle)
|
||||
const asideBrandStyle = useAppSelector((state) => state.style.asideBrandStyle)
|
||||
const asideScrollbarsStyle = useAppSelector((state) => state.style.asideScrollbarsStyle)
|
||||
const darkMode = useAppSelector((state) => state.style.darkMode)
|
||||
const { currentUser } = useAppSelector((state) => state.auth)
|
||||
|
||||
const [organizations, setOrganizations] = React.useState<OrganizationOption[]>([])
|
||||
|
||||
const roleName = currentUser?.app_role?.name || ''
|
||||
const workspaceConfig = getWorkspaceConfig(roleName)
|
||||
const workspaceHref = getWorkspaceRoute(roleName)
|
||||
const organizationId = currentUser?.organizations?.id
|
||||
|
||||
React.useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
const fetchOrganizations = async () => {
|
||||
try {
|
||||
const response = await axios.get('/org-for-auth')
|
||||
|
||||
if (mounted) {
|
||||
setOrganizations(Array.isArray(response.data) ? response.data : [])
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Failed to load organizations for sidebar', error?.response || error)
|
||||
}
|
||||
}
|
||||
|
||||
fetchOrganizations()
|
||||
|
||||
return () => {
|
||||
mounted = false
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleAsideLgCloseClick = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
props.onAsideLgCloseClick()
|
||||
}
|
||||
|
||||
const organizationName = React.useMemo(() => {
|
||||
const matchedOrganization = organizations.find((item) => item.id === organizationId)?.name
|
||||
const dispatch = useAppDispatch();
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
const organizationsId = currentUser?.organizations?.id;
|
||||
const [organizations, setOrganizations] = React.useState(null);
|
||||
|
||||
if (!matchedOrganization) {
|
||||
return 'Organization workspace'
|
||||
const fetchOrganizations = createAsyncThunk('/org-for-auth', async () => {
|
||||
try {
|
||||
const response = await axios.get('/org-for-auth');
|
||||
setOrganizations(response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(error.response);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchOrganizations());
|
||||
}, [dispatch]);
|
||||
|
||||
let organizationName = organizations?.find(item => item.id === organizationsId)?.name;
|
||||
if(organizationName?.length > 25){
|
||||
organizationName = organizationName?.substring(0, 25) + '...';
|
||||
}
|
||||
|
||||
return matchedOrganization.length > 26 ? `${matchedOrganization.substring(0, 26)}...` : matchedOrganization
|
||||
}, [organizationId, organizations])
|
||||
|
||||
return (
|
||||
<aside
|
||||
id='asideMenu'
|
||||
className={`${className} fixed top-0 z-40 flex h-screen w-72 overflow-hidden transition-position lg:py-2 lg:pl-2`}
|
||||
className={`${className} zzz lg:py-2 lg:pl-2 w-60 fixed flex z-40 top-0 h-screen transition-position overflow-hidden`}
|
||||
>
|
||||
<div className={`flex flex-1 flex-col overflow-hidden border ${asideStyle} ${corners}`}>
|
||||
<div className={`border-b px-4 py-4 ${asideBrandStyle}`}>
|
||||
<div className='flex items-start justify-between gap-3'>
|
||||
<div className='min-w-0 flex-1'>
|
||||
<p className='text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-500 dark:text-slate-400'>FDSU ERP</p>
|
||||
<h2 className='mt-2 line-clamp-2 text-lg font-semibold leading-6 text-slate-900 dark:text-white'>
|
||||
{workspaceConfig.sidebarLabel}
|
||||
</h2>
|
||||
<div className='mt-3 flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400'>
|
||||
<BaseIcon path={mdiOfficeBuildingOutline} size={14} />
|
||||
<span className='line-clamp-2 leading-5'>{organizationName}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button className='hidden rounded-lg p-2 text-slate-500 transition hover:bg-slate-100 hover:text-slate-900 lg:inline-flex xl:hidden dark:hover:bg-slate-900 dark:hover:text-white' onClick={handleAsideLgCloseClick}>
|
||||
<BaseIcon path={mdiClose} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='mt-4 border-t border-slate-200 pt-3 dark:border-slate-800'>
|
||||
<p className='truncate text-sm font-semibold text-slate-900 dark:text-white'>
|
||||
{currentUser?.firstName || currentUser?.email || 'Authenticated user'}
|
||||
</p>
|
||||
<div className='mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-slate-500 dark:text-slate-400'>
|
||||
<span>{roleName || 'Workspace access'}</span>
|
||||
<span aria-hidden='true'>•</span>
|
||||
<Link href={workspaceHref} className='inline-flex items-center gap-1 font-medium text-blue-700 transition hover:text-slate-950 dark:text-sky-400 dark:hover:text-white'>
|
||||
Open workspace home
|
||||
<BaseIcon path={mdiArrowTopRight} size={14} />
|
||||
</Link>
|
||||
</div>
|
||||
<div
|
||||
className={`flex-1 flex flex-col overflow-hidden dark:bg-dark-900 ${asideStyle} ${corners}`}
|
||||
>
|
||||
<div
|
||||
className={`flex flex-row h-14 items-center justify-between ${asideBrandStyle}`}
|
||||
>
|
||||
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
||||
|
||||
<b className="font-black">FDSU ERP</b>
|
||||
|
||||
|
||||
{organizationName && <p>{organizationName}</p>}
|
||||
|
||||
</div>
|
||||
<button
|
||||
className="hidden lg:inline-block xl:hidden p-3"
|
||||
onClick={handleAsideLgCloseClick}
|
||||
>
|
||||
<BaseIcon path={mdiClose} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={`flex-1 overflow-y-auto overflow-x-hidden px-2 py-3 ${darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle}`}>
|
||||
<div
|
||||
className={`flex-1 overflow-y-auto overflow-x-hidden ${
|
||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
||||
}`}
|
||||
>
|
||||
<AsideMenuList menu={menu} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import React from 'react'
|
||||
import { MenuAsideItem } from '../interfaces'
|
||||
import AsideMenuItem from './AsideMenuItem'
|
||||
import { useAppSelector } from '../stores/hooks';
|
||||
import { hasPermission } from '../helpers/userPermissions';
|
||||
import { getWorkspaceConfig, getWorkspaceRoute, itemVisibleForRole } from '../helpers/workspace';
|
||||
import {useAppSelector} from "../stores/hooks";
|
||||
import {hasPermission} from "../helpers/userPermissions";
|
||||
|
||||
type Props = {
|
||||
menu: MenuAsideItem[]
|
||||
@ -16,29 +15,16 @@ export default function AsideMenuList({ menu, isDropdownList = false, className
|
||||
|
||||
if (!currentUser) return null;
|
||||
|
||||
const roleName = currentUser?.app_role?.name;
|
||||
const workspaceConfig = getWorkspaceConfig(roleName);
|
||||
|
||||
return (
|
||||
<ul className={className}>
|
||||
{menu.map((item, index) => {
|
||||
if (!itemVisibleForRole(item.roles, roleName)) return null;
|
||||
if (!hasPermission(currentUser, item.permissions)) return null;
|
||||
|
||||
const displayItem: MenuAsideItem = {
|
||||
...item,
|
||||
label: item.labelByRole?.[roleName] || item.label,
|
||||
};
|
||||
|
||||
if (displayItem.label === 'Role Workspace') {
|
||||
displayItem.label = workspaceConfig.sidebarLabel;
|
||||
displayItem.href = getWorkspaceRoute(roleName);
|
||||
}
|
||||
|
||||
|
||||
if (!hasPermission(currentUser, item.permissions)) return null;
|
||||
|
||||
return (
|
||||
<div key={index}>
|
||||
<AsideMenuItem
|
||||
item={displayItem}
|
||||
item={item}
|
||||
isDropdownList={isDropdownList}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,96 +0,0 @@
|
||||
import { mdiArrowLeft, mdiEyeOutline } from '@mdi/js';
|
||||
import React from 'react';
|
||||
import BaseButton from '../BaseButton';
|
||||
import BaseButtons from '../BaseButtons';
|
||||
import CardBox from '../CardBox';
|
||||
import {
|
||||
emptyValue,
|
||||
formatPrimitiveValue,
|
||||
getPrimaryTitle,
|
||||
getRecordSubtitle,
|
||||
getSummaryEntries,
|
||||
humanizeLabel,
|
||||
} from '../EntityPageUtils';
|
||||
|
||||
type Props = {
|
||||
entityLabel: string;
|
||||
pluralLabel: string;
|
||||
listHref: string;
|
||||
viewHref?: string;
|
||||
record: any;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const EnhancedEntityEditShell = ({ entityLabel, pluralLabel, listHref, viewHref, record, children }: Props) => {
|
||||
const title = getPrimaryTitle(record, `Edit ${entityLabel}`);
|
||||
const subtitle = getRecordSubtitle(record, `${entityLabel} record`);
|
||||
const summaryEntries = getSummaryEntries(record, 7);
|
||||
|
||||
return (
|
||||
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_320px] xl:items-start">
|
||||
<div className="rounded-2xl border border-slate-200/80 bg-white p-5 shadow-sm dark:border-dark-700 dark:bg-dark-900">
|
||||
<div className="mb-6 rounded-xl border border-slate-200/80 bg-slate-50/80 p-5 dark:border-dark-700 dark:bg-dark-800/60">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="max-w-2xl">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">Edit workspace</p>
|
||||
<h3 className="mt-2 text-2xl font-semibold text-slate-900 dark:text-slate-100">{title}</h3>
|
||||
<p className="mt-2 text-sm text-slate-600 dark:text-slate-300">{subtitle}. Update the fields below and keep the record details clear, complete, and ready for downstream users.</p>
|
||||
</div>
|
||||
<BaseButtons type="justify-start md:justify-end" className="w-full lg:w-auto" noWrap>
|
||||
<BaseButton color="white" outline icon={mdiArrowLeft} label={`Back to ${pluralLabel.toLowerCase()}`} href={listHref} />
|
||||
{viewHref ? <BaseButton color="white" outline icon={mdiEyeOutline} label={`View ${entityLabel.toLowerCase()}`} href={viewHref} /> : null}
|
||||
</BaseButtons>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2 [&>div:not(:last-child)]:min-w-0 [&>div:not(:last-child)]:rounded-xl [&>div:not(:last-child)]:border [&>div:not(:last-child)]:border-slate-200/80 [&>div:not(:last-child)]:bg-slate-50/50 [&>div:not(:last-child)]:p-4 dark:[&>div:not(:last-child)]:border-dark-700 dark:[&>div:not(:last-child)]:bg-dark-800/40 [&>hr]:md:col-span-2 [&>div:last-child]:md:col-span-2">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6 xl:sticky xl:top-6">
|
||||
<CardBox>
|
||||
<div className="space-y-5">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-lg border border-slate-200 bg-slate-50 p-2 text-slate-500 dark:border-dark-700 dark:bg-dark-800 dark:text-slate-300">
|
||||
<span className="inline-flex"><svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M4 19H20V21H4V19M17 7L15 5L17 3L19 5L17 7M6 17H4V15L14 5L16 7L6 17Z" /></svg></span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100">Editing summary</h3>
|
||||
<p className="mt-1 text-sm text-slate-600 dark:text-slate-300">Key values stay visible while the form is being updated.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{summaryEntries.length > 0 ? (
|
||||
summaryEntries.map(([key, value]) => (
|
||||
<div key={key} className="rounded-xl border border-slate-200/80 bg-slate-50/70 p-4 dark:border-dark-700 dark:bg-dark-800/60">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">{humanizeLabel(key)}</p>
|
||||
<div className="mt-2 text-sm font-medium text-slate-900 dark:text-slate-100">{value instanceof Date || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ? formatPrimitiveValue(key, value) : value || emptyValue}</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="rounded-xl border border-dashed border-slate-200 bg-slate-50/70 px-4 py-6 text-sm text-slate-600 dark:border-dark-700 dark:bg-dark-800/50 dark:text-slate-300">
|
||||
Summary fields will appear here as soon as this record has saved content.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<CardBox>
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100">Editing guidance</h3>
|
||||
<ul className="space-y-2 text-sm text-slate-600 dark:text-slate-300">
|
||||
<li>• Keep naming and reference fields consistent with list and dashboard views.</li>
|
||||
<li>• Use notes and description fields to add decision-quality context, not placeholders.</li>
|
||||
<li>• Reset only if you want to discard unsaved changes in this form session.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</CardBox>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnhancedEntityEditShell;
|
||||
@ -1,282 +0,0 @@
|
||||
import { mdiArrowLeft, mdiChartTimelineVariant, mdiPencil } from '@mdi/js';
|
||||
import Head from 'next/head';
|
||||
import React, { useEffect } from 'react';
|
||||
import BaseButton from '../BaseButton';
|
||||
import BaseButtons from '../BaseButtons';
|
||||
import BaseIcon from '../BaseIcon';
|
||||
import CardBox from '../CardBox';
|
||||
import SectionMain from '../SectionMain';
|
||||
import SectionTitleLineWithButton from '../SectionTitleLineWithButton';
|
||||
import { getPageTitle } from '../../config';
|
||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import {
|
||||
emptyValue,
|
||||
formatPrimitiveValue,
|
||||
getChipEntries,
|
||||
getCollectionPreview,
|
||||
getMetricCards,
|
||||
getPrimaryTitle,
|
||||
getRecordEntries,
|
||||
getRecordSubtitle,
|
||||
getRelationLabel,
|
||||
getSummaryEntries,
|
||||
humanizeLabel,
|
||||
isRecordObject,
|
||||
} from '../EntityPageUtils';
|
||||
|
||||
type Props = {
|
||||
singularLabel: string;
|
||||
pluralLabel: string;
|
||||
stateKey: string;
|
||||
recordKey: string;
|
||||
fetchRecord: any;
|
||||
listHref: string;
|
||||
editHref: (id: string | string[] | undefined) => string;
|
||||
};
|
||||
|
||||
const getBadgeClassName = (value?: string | null) => {
|
||||
switch (String(value || '').toLowerCase()) {
|
||||
case 'active':
|
||||
case 'approved':
|
||||
case 'completed':
|
||||
case 'resolved':
|
||||
case 'settled':
|
||||
case 'paid':
|
||||
case 'enabled':
|
||||
case 'true':
|
||||
return 'border-emerald-200 bg-emerald-50 text-emerald-700';
|
||||
case 'submitted':
|
||||
case 'under_review':
|
||||
case 'in_progress':
|
||||
case 'validated':
|
||||
case 'partial':
|
||||
case 'draft':
|
||||
return 'border-blue-200 bg-blue-50 text-blue-700';
|
||||
case 'on_hold':
|
||||
case 'blocked':
|
||||
case 'follow_up_required':
|
||||
case 'disputed':
|
||||
case 'pending':
|
||||
return 'border-amber-200 bg-amber-50 text-amber-700';
|
||||
case 'critical':
|
||||
case 'high':
|
||||
case 'terminated':
|
||||
case 'cancelled':
|
||||
case 'closed':
|
||||
case 'disabled':
|
||||
case 'false':
|
||||
return 'border-red-200 bg-red-50 text-red-700';
|
||||
default:
|
||||
return 'border-slate-200 bg-slate-50 text-slate-700 dark:border-dark-700 dark:bg-dark-800 dark:text-slate-200';
|
||||
}
|
||||
};
|
||||
|
||||
const StatusBadge = ({ value }: { value: any }) => (
|
||||
<span className={`rounded-full border px-2.5 py-1 text-xs font-semibold ${getBadgeClassName(value)}`}>{String(formatPrimitiveValue('status', value))}</span>
|
||||
);
|
||||
|
||||
const DetailItem = ({ label, value }: { label: string; value: React.ReactNode }) => (
|
||||
<div className="rounded-xl border border-slate-200/80 bg-slate-50/70 p-4 dark:border-dark-700 dark:bg-dark-800/60">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">{label}</p>
|
||||
<div className="mt-2 text-sm leading-6 text-slate-900 dark:text-slate-100">{value || emptyValue}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const MetricCard = ({ label, value, note }: { label: string; value: string; note: string }) => (
|
||||
<div className="rounded-xl border border-slate-200/80 bg-slate-50/80 p-4 dark:border-dark-700 dark:bg-dark-800/60">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">{label}</p>
|
||||
<p className="mt-3 text-2xl font-semibold text-slate-900 dark:text-slate-100">{value}</p>
|
||||
<p className="mt-2 text-sm text-slate-600 dark:text-slate-300">{note}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const EntityRecordViewPage = ({ singularLabel, pluralLabel, stateKey, recordKey, fetchRecord, listHref, editHref }: Props) => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const { id } = router.query;
|
||||
const sliceState = useAppSelector((state) => (state as any)[stateKey]);
|
||||
const record = sliceState?.[recordKey];
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
dispatch(fetchRecord({ id }));
|
||||
}
|
||||
}, [dispatch, fetchRecord, id]);
|
||||
|
||||
const recordTitle = getPrimaryTitle(record, `View ${singularLabel}`);
|
||||
const recordSubtitle = getRecordSubtitle(record, `${singularLabel} details`);
|
||||
const summaryEntries = getSummaryEntries(record, 8);
|
||||
const metricCards = getMetricCards(record);
|
||||
const chipEntries = getChipEntries(record);
|
||||
const { scalarEntries, relationEntries, collectionEntries } = getRecordEntries(record);
|
||||
const overviewEntries = summaryEntries;
|
||||
const detailEntries = scalarEntries.filter(([key]) => !overviewEntries.some(([entryKey]) => entryKey === key));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle(recordTitle)}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={recordTitle} main>
|
||||
<BaseButtons type="justify-start md:justify-end" className="w-full md:w-auto" noWrap>
|
||||
<BaseButton color="white" outline icon={mdiArrowLeft} label={`Back to ${pluralLabel.toLowerCase()}`} onClick={() => router.push(listHref)} />
|
||||
<BaseButton color="info" icon={mdiPencil} label={`Edit ${singularLabel.toLowerCase()}`} href={editHref(id)} />
|
||||
</BaseButtons>
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<div className="space-y-6">
|
||||
<CardBox>
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="max-w-3xl">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">{singularLabel} workspace</p>
|
||||
<h2 className="mt-2 text-3xl font-semibold text-slate-900 dark:text-slate-100">{recordTitle}</h2>
|
||||
<p className="mt-3 text-sm text-slate-600 dark:text-slate-300">{recordSubtitle}</p>
|
||||
</div>
|
||||
{chipEntries.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{chipEntries.map(([key, value]) => (
|
||||
<div key={key} className="flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-1 dark:border-dark-700 dark:bg-dark-900">
|
||||
<span className="text-xs font-semibold uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{humanizeLabel(key)}</span>
|
||||
<StatusBadge value={value} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{metricCards.map((metric) => (
|
||||
<MetricCard key={metric.label} label={metric.label} value={metric.value} note={metric.note} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
{!isRecordObject(record) ? (
|
||||
<CardBox>
|
||||
<div className="rounded-xl border border-dashed border-slate-200 bg-slate-50/70 px-4 py-8 text-sm text-slate-600 dark:border-dark-700 dark:bg-dark-800/50 dark:text-slate-300">
|
||||
Loading {singularLabel.toLowerCase()} details or waiting for the record to become available.
|
||||
</div>
|
||||
</CardBox>
|
||||
) : (
|
||||
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px]">
|
||||
<div className="space-y-6">
|
||||
<CardBox>
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100">Overview</h3>
|
||||
<p className="mt-1 text-sm text-slate-600 dark:text-slate-300">Key information is grouped here for faster review and cleaner scanning.</p>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{overviewEntries.map(([key, value]) => (
|
||||
<DetailItem key={key} label={humanizeLabel(key)} value={formatPrimitiveValue(key, value)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
{detailEntries.length > 0 && (
|
||||
<CardBox>
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100">Extended details</h3>
|
||||
<p className="mt-1 text-sm text-slate-600 dark:text-slate-300">Additional record content is still available, but presented in a cleaner structured layout.</p>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{detailEntries.map(([key, value]) => (
|
||||
<DetailItem key={key} label={humanizeLabel(key)} value={formatPrimitiveValue(key, value)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
)}
|
||||
|
||||
{collectionEntries.map(([key, value]) => {
|
||||
const items = Array.isArray(value) ? value : [];
|
||||
const preview = getCollectionPreview(items);
|
||||
|
||||
return (
|
||||
<CardBox key={key}>
|
||||
<div className="space-y-5">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100">{humanizeLabel(key)}</h3>
|
||||
<p className="mt-1 text-sm text-slate-600 dark:text-slate-300">Related records are presented as compact cards instead of a raw data dump.</p>
|
||||
</div>
|
||||
<span className="rounded-full border border-slate-200 bg-slate-50 px-3 py-1 text-xs font-semibold uppercase tracking-[0.14em] text-slate-600 dark:border-dark-700 dark:bg-dark-800 dark:text-slate-300">
|
||||
{items.length} records
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{preview.length > 0 ? (
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{preview.map((item) => (
|
||||
<div key={item.key} className="rounded-xl border border-slate-200/80 bg-slate-50/70 p-4 dark:border-dark-700 dark:bg-dark-800/60">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100">{item.title}</p>
|
||||
<div className="rounded-lg border border-slate-200 bg-white p-2 text-slate-500 dark:border-dark-700 dark:bg-dark-900 dark:text-slate-300">
|
||||
<BaseIcon path={mdiChartTimelineVariant} size={18} />
|
||||
</div>
|
||||
</div>
|
||||
{item.details.length > 0 ? (
|
||||
<dl className="mt-4 space-y-3">
|
||||
{item.details.map((detail) => (
|
||||
<div key={`${item.key}-${detail.label}`}>
|
||||
<dt className="text-xs font-semibold uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{detail.label}</dt>
|
||||
<dd className="mt-1 text-sm text-slate-700 dark:text-slate-200">{detail.value || emptyValue}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
) : (
|
||||
<p className="mt-4 text-sm text-slate-600 dark:text-slate-300">No additional preview fields are available for this related record.</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-xl border border-dashed border-slate-200 bg-slate-50/70 px-4 py-6 text-sm text-slate-600 dark:border-dark-700 dark:bg-dark-800/50 dark:text-slate-300">
|
||||
No related records have been attached here yet.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardBox>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<CardBox>
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100">Connected records</h3>
|
||||
<p className="mt-1 text-sm text-slate-600 dark:text-slate-300">Direct linked records are summarized here so users can see context without parsing nested JSON-like sections.</p>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{relationEntries.length > 0 ? (
|
||||
relationEntries.map(([key, value]) => (
|
||||
<div key={key} className="rounded-xl border border-slate-200/80 bg-slate-50/70 p-4 dark:border-dark-700 dark:bg-dark-800/60">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">{humanizeLabel(key)}</p>
|
||||
<div className="mt-2 text-sm font-medium text-slate-900 dark:text-slate-100">{getRelationLabel(value)}</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="rounded-xl border border-dashed border-slate-200 bg-slate-50/70 px-4 py-6 text-sm text-slate-600 dark:border-dark-700 dark:bg-dark-800/50 dark:text-slate-300">
|
||||
No linked records are available on this item yet.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EntityRecordViewPage;
|
||||
@ -1,317 +0,0 @@
|
||||
import React from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const EMPTY_VALUE = 'Not yet recorded';
|
||||
const SYSTEM_FIELDS = new Set([
|
||||
'id',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'deletedAt',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at',
|
||||
'password',
|
||||
'passwordHash',
|
||||
'salt',
|
||||
]);
|
||||
|
||||
const PRIMARY_TITLE_FIELDS = [
|
||||
'name',
|
||||
'title',
|
||||
'label',
|
||||
'subject',
|
||||
'project_name',
|
||||
'program_name',
|
||||
'contract_title',
|
||||
'contract_number',
|
||||
'code',
|
||||
'project_code',
|
||||
'email',
|
||||
'firstName',
|
||||
'lastName',
|
||||
];
|
||||
|
||||
const SUMMARY_FIELD_PRIORITY = [
|
||||
'name',
|
||||
'title',
|
||||
'label',
|
||||
'code',
|
||||
'project_code',
|
||||
'contract_number',
|
||||
'status',
|
||||
'state',
|
||||
'phase',
|
||||
'priority',
|
||||
'risk_level',
|
||||
'type',
|
||||
'category',
|
||||
'organization',
|
||||
'program',
|
||||
'project',
|
||||
'vendor',
|
||||
'department',
|
||||
'assigned_to',
|
||||
'requested_by',
|
||||
'requested_at',
|
||||
'due_date',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'amount',
|
||||
'budget_amount',
|
||||
'contract_value',
|
||||
'currency',
|
||||
];
|
||||
|
||||
const CHIP_FIELDS = ['status', 'state', 'phase', 'priority', 'risk_level', 'type', 'category'];
|
||||
|
||||
const DATE_SUFFIXES = ['_at', '_date'];
|
||||
const DATE_KEYWORDS = ['date', 'time', 'deadline'];
|
||||
const AMOUNT_KEYWORDS = ['amount', 'value', 'cost', 'budget', 'total', 'price', 'percent'];
|
||||
|
||||
export const emptyValue = EMPTY_VALUE;
|
||||
|
||||
export const titleCase = (value?: string | null) => {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return value
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, (match) => match.toUpperCase());
|
||||
};
|
||||
|
||||
export const singularizeLabel = (value: string) => {
|
||||
if (value.endsWith('ies')) {
|
||||
return `${value.slice(0, -3)}y`;
|
||||
}
|
||||
|
||||
if (value.endsWith('ches') || value.endsWith('shes') || value.endsWith('xes') || value.endsWith('zes')) {
|
||||
return value.slice(0, -2);
|
||||
}
|
||||
|
||||
if (value.endsWith('ses') && !value.endsWith('issues') && !value.endsWith('buses')) {
|
||||
return value.slice(0, -1);
|
||||
}
|
||||
|
||||
if (value.endsWith('s')) {
|
||||
return value.slice(0, -1);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const humanizeLabel = (key: string) => {
|
||||
return titleCase(
|
||||
key
|
||||
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\bid\b/i, 'ID')
|
||||
.trim(),
|
||||
);
|
||||
};
|
||||
|
||||
export const isRecordObject = (value: any) => {
|
||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date);
|
||||
};
|
||||
|
||||
export const isDateLikeKey = (key: string) => {
|
||||
return DATE_SUFFIXES.some((suffix) => key.endsWith(suffix)) || DATE_KEYWORDS.some((part) => key.includes(part));
|
||||
};
|
||||
|
||||
export const isAmountLikeKey = (key: string) => {
|
||||
return AMOUNT_KEYWORDS.some((part) => key.includes(part));
|
||||
};
|
||||
|
||||
export const stripHtml = (value: string) => value.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
|
||||
export const formatPrimitiveValue = (key: string, value: any): React.ReactNode => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return EMPTY_VALUE;
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'Yes' : 'No';
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
if (key.toLowerCase().includes('percent')) {
|
||||
return `${value}%`;
|
||||
}
|
||||
|
||||
return value.toLocaleString();
|
||||
}
|
||||
|
||||
if (value instanceof Date || (typeof value === 'string' && isDateLikeKey(key) && dayjs(value).isValid())) {
|
||||
return dayjs(value).format('DD MMM YYYY HH:mm');
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
if (value.trim().startsWith('<') && value.includes('>')) {
|
||||
const stripped = stripHtml(value);
|
||||
return stripped || EMPTY_VALUE;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return String(value);
|
||||
};
|
||||
|
||||
export const getRelationLabel = (value: any) => {
|
||||
if (!isRecordObject(value)) {
|
||||
return EMPTY_VALUE;
|
||||
}
|
||||
|
||||
const candidates = ['name', 'title', 'label', 'code', 'project_code', 'contract_number', 'email', 'id'];
|
||||
for (const candidate of candidates) {
|
||||
if (value[candidate]) {
|
||||
return formatPrimitiveValue(candidate, value[candidate]);
|
||||
}
|
||||
}
|
||||
|
||||
const visibleEntry = Object.entries(value).find(([entryKey, entryValue]) => !SYSTEM_FIELDS.has(entryKey) && !isRecordObject(entryValue) && !Array.isArray(entryValue));
|
||||
return visibleEntry ? formatPrimitiveValue(visibleEntry[0], visibleEntry[1]) : EMPTY_VALUE;
|
||||
};
|
||||
|
||||
export const getPrimaryTitle = (record: any, fallback: string) => {
|
||||
if (!isRecordObject(record)) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const firstName = record.firstName || record.first_name;
|
||||
const lastName = record.lastName || record.last_name;
|
||||
if (firstName || lastName) {
|
||||
return [firstName, lastName].filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
for (const field of PRIMARY_TITLE_FIELDS) {
|
||||
if (record[field]) {
|
||||
const value = formatPrimitiveValue(field, record[field]);
|
||||
return typeof value === 'string' ? value : fallback;
|
||||
}
|
||||
}
|
||||
|
||||
return fallback;
|
||||
};
|
||||
|
||||
export const getRecordSubtitle = (record: any, fallback: string) => {
|
||||
if (!isRecordObject(record)) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const referenceFields = ['code', 'project_code', 'contract_number', 'email', 'id'];
|
||||
const reference = referenceFields.find((field) => record[field]);
|
||||
if (!reference) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return `${humanizeLabel(reference)}: ${formatPrimitiveValue(reference, record[reference])}`;
|
||||
};
|
||||
|
||||
export const getRecordEntries = (record: any) => {
|
||||
if (!isRecordObject(record)) {
|
||||
return { scalarEntries: [], relationEntries: [], collectionEntries: [] };
|
||||
}
|
||||
|
||||
const scalarEntries = Object.entries(record).filter(([key, value]) => {
|
||||
return !SYSTEM_FIELDS.has(key) && !Array.isArray(value) && !isRecordObject(value);
|
||||
});
|
||||
|
||||
const relationEntries = Object.entries(record).filter(([key, value]) => {
|
||||
return !SYSTEM_FIELDS.has(key) && isRecordObject(value);
|
||||
});
|
||||
|
||||
const collectionEntries = Object.entries(record).filter(([key, value]) => {
|
||||
return !SYSTEM_FIELDS.has(key) && Array.isArray(value);
|
||||
});
|
||||
|
||||
return { scalarEntries, relationEntries, collectionEntries };
|
||||
};
|
||||
|
||||
export const sortEntries = (entries: Array<[string, any]>) => {
|
||||
return [...entries].sort((left, right) => {
|
||||
const leftIndex = SUMMARY_FIELD_PRIORITY.indexOf(left[0]);
|
||||
const rightIndex = SUMMARY_FIELD_PRIORITY.indexOf(right[0]);
|
||||
|
||||
if (leftIndex !== -1 || rightIndex !== -1) {
|
||||
return (leftIndex === -1 ? 999 : leftIndex) - (rightIndex === -1 ? 999 : rightIndex);
|
||||
}
|
||||
|
||||
return humanizeLabel(left[0]).localeCompare(humanizeLabel(right[0]));
|
||||
});
|
||||
};
|
||||
|
||||
export const getSummaryEntries = (record: any, limit = 8) => {
|
||||
const { scalarEntries, relationEntries } = getRecordEntries(record);
|
||||
const scalar = sortEntries(scalarEntries);
|
||||
const relation = sortEntries(relationEntries)
|
||||
.filter(([key]) => !CHIP_FIELDS.includes(key))
|
||||
.map(([key, value]) => [key, getRelationLabel(value)] as [string, React.ReactNode]);
|
||||
|
||||
return [...scalar, ...relation].slice(0, limit);
|
||||
};
|
||||
|
||||
export const getChipEntries = (record: any) => {
|
||||
if (!isRecordObject(record)) {
|
||||
return [] as Array<[string, any]>;
|
||||
}
|
||||
|
||||
return CHIP_FIELDS.filter((field) => record[field]).map((field) => [field, record[field]] as [string, any]);
|
||||
};
|
||||
|
||||
export const getMetricCards = (record: any) => {
|
||||
const { collectionEntries } = getRecordEntries(record);
|
||||
const metrics = [
|
||||
{
|
||||
label: 'Linked sections',
|
||||
value: collectionEntries.length.toString(),
|
||||
note: collectionEntries.length ? 'Related collections are shown below.' : 'No related collections are attached yet.',
|
||||
},
|
||||
];
|
||||
|
||||
const importantEntries = [
|
||||
['status', record?.status],
|
||||
['start_date', record?.start_date],
|
||||
['end_date', record?.end_date],
|
||||
['budget_amount', record?.budget_amount ?? record?.amount ?? record?.contract_value],
|
||||
].filter(([, value]) => value !== undefined && value !== null && value !== '');
|
||||
|
||||
for (const [key, value] of importantEntries.slice(0, 3)) {
|
||||
metrics.push({
|
||||
label: humanizeLabel(key),
|
||||
value: String(formatPrimitiveValue(key, value)),
|
||||
note: key === 'budget_amount' ? 'Tracked financial figure for this record.' : 'Key operating signal for this record.',
|
||||
});
|
||||
}
|
||||
|
||||
return metrics.slice(0, 4);
|
||||
};
|
||||
|
||||
export const getCollectionPreview = (items: any[]) => {
|
||||
return items.slice(0, 6).map((item, index) => {
|
||||
if (!isRecordObject(item)) {
|
||||
return {
|
||||
key: String(index),
|
||||
title: formatPrimitiveValue('value', item),
|
||||
details: [],
|
||||
};
|
||||
}
|
||||
|
||||
const title = getPrimaryTitle(item, `Record ${index + 1}`);
|
||||
const details = sortEntries(
|
||||
Object.entries(item).filter(([key, value]) => !SYSTEM_FIELDS.has(key) && !Array.isArray(value) && !isRecordObject(value)),
|
||||
)
|
||||
.filter(([key]) => !PRIMARY_TITLE_FIELDS.includes(key))
|
||||
.slice(0, 4)
|
||||
.map(([key, value]) => ({
|
||||
label: humanizeLabel(key),
|
||||
value: formatPrimitiveValue(key, value),
|
||||
}));
|
||||
|
||||
return {
|
||||
key: String(item.id || index),
|
||||
title,
|
||||
details,
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||
import BaseDivider from './BaseDivider'
|
||||
import BaseIcon from './BaseIcon'
|
||||
|
||||
@ -35,7 +35,7 @@ const TableSampleClients = () => {
|
||||
return (
|
||||
<>
|
||||
<CardBoxModal
|
||||
title="Record preview"
|
||||
title="Sample modal"
|
||||
buttonColor="info"
|
||||
buttonLabel="Done"
|
||||
isActive={isModalInfoActive}
|
||||
@ -45,7 +45,7 @@ const TableSampleClients = () => {
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet <b>adipiscing elit</b>
|
||||
</p>
|
||||
<p>This is a preview modal.</p>
|
||||
<p>This is sample modal</p>
|
||||
</CardBoxModal>
|
||||
|
||||
<CardBoxModal
|
||||
@ -59,7 +59,7 @@ const TableSampleClients = () => {
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet <b>adipiscing elit</b>
|
||||
</p>
|
||||
<p>This is a preview modal.</p>
|
||||
<p>This is sample modal</p>
|
||||
</CardBoxModal>
|
||||
|
||||
<table>
|
||||
|
||||
@ -3,7 +3,7 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
@apply pt-14 xl:pl-72 h-full;
|
||||
@apply pt-14 xl:pl-60 h-full;
|
||||
}
|
||||
|
||||
#app {
|
||||
|
||||
@ -1,820 +0,0 @@
|
||||
export const WORKSPACE_ROLES = {
|
||||
superAdmin: 'Super Administrator',
|
||||
administrator: 'Administrator',
|
||||
directorGeneral: 'Director General',
|
||||
financeDirector: 'Finance Director',
|
||||
procurementLead: 'Procurement Lead',
|
||||
complianceAuditLead: 'Compliance and Audit Lead',
|
||||
projectDeliveryLead: 'Project Delivery Lead',
|
||||
public: 'Public',
|
||||
} as const;
|
||||
|
||||
export const WORKSPACE_ROUTES = {
|
||||
[WORKSPACE_ROLES.superAdmin]: '/platform-command',
|
||||
[WORKSPACE_ROLES.administrator]: '/operations-command',
|
||||
[WORKSPACE_ROLES.directorGeneral]: '/executive-command',
|
||||
[WORKSPACE_ROLES.financeDirector]: '/financial-control',
|
||||
[WORKSPACE_ROLES.procurementLead]: '/procurement-desk',
|
||||
[WORKSPACE_ROLES.complianceAuditLead]: '/compliance-desk',
|
||||
[WORKSPACE_ROLES.projectDeliveryLead]: '/delivery-command',
|
||||
} as const;
|
||||
|
||||
const ADMIN_DASHBOARD_ROLES = [WORKSPACE_ROLES.superAdmin, WORKSPACE_ROLES.administrator] as const;
|
||||
|
||||
export type WorkspaceMetricKey =
|
||||
| 'approvedBudget'
|
||||
| 'committedBudget'
|
||||
| 'disbursedBudget'
|
||||
| 'pendingApprovals'
|
||||
| 'contractsNearingExpiry'
|
||||
| 'vendorComplianceAlerts'
|
||||
| 'procurementPipeline'
|
||||
| 'openRiskAlerts'
|
||||
| 'averageProjectProgress'
|
||||
| 'highRiskProjects'
|
||||
| 'overduePayments'
|
||||
| 'activeProjects'
|
||||
| 'unreadNotifications'
|
||||
| 'organizationCount'
|
||||
| 'platformUserCount'
|
||||
| 'roleCount'
|
||||
| 'permissionCount'
|
||||
| 'auditEventCount';
|
||||
|
||||
export type WorkspaceDetailBlockKey =
|
||||
| 'focus'
|
||||
| 'summary'
|
||||
| 'watchlist'
|
||||
| 'approvalRisk'
|
||||
| 'operations'
|
||||
| 'delivery'
|
||||
| 'actions';
|
||||
|
||||
export type WorkspaceSectionKey =
|
||||
| 'approvalQueue'
|
||||
| 'riskPanel'
|
||||
| 'procurementQueue'
|
||||
| 'contractWatchlist'
|
||||
| 'recentNotifications'
|
||||
| 'provinceRollout'
|
||||
| 'topContracts'
|
||||
| 'quickActions';
|
||||
|
||||
export type WorkspaceQuickLinkIconKey =
|
||||
| 'organizations'
|
||||
| 'users'
|
||||
| 'approvals'
|
||||
| 'notifications'
|
||||
| 'projects'
|
||||
| 'contracts'
|
||||
| 'payments'
|
||||
| 'allocations'
|
||||
| 'requisitions'
|
||||
| 'tenders'
|
||||
| 'compliance'
|
||||
| 'audit'
|
||||
| 'milestones'
|
||||
| 'vendors';
|
||||
|
||||
export interface WorkspaceAction {
|
||||
href: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceQuickLink {
|
||||
href: string;
|
||||
label: string;
|
||||
description: string;
|
||||
icon: WorkspaceQuickLinkIconKey;
|
||||
}
|
||||
|
||||
export interface WorkspaceSectionCopy {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
actionLabel?: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceBriefingCard {
|
||||
title: string;
|
||||
items: string[];
|
||||
}
|
||||
|
||||
export interface WorkspaceConfig {
|
||||
sidebarLabel: string;
|
||||
pageTitle: string;
|
||||
eyebrow: string;
|
||||
heroTitle: string;
|
||||
heroDescription: string;
|
||||
primaryAction: WorkspaceAction;
|
||||
secondaryAction: WorkspaceAction;
|
||||
briefingCards: WorkspaceBriefingCard[];
|
||||
highlightedMetricKeys: WorkspaceMetricKey[];
|
||||
heroMetricKeys: WorkspaceMetricKey[];
|
||||
blockOrder: WorkspaceDetailBlockKey[];
|
||||
sectionCopy: Record<WorkspaceSectionKey, WorkspaceSectionCopy>;
|
||||
quickLinks: WorkspaceQuickLink[];
|
||||
}
|
||||
|
||||
type WorkspaceConfigInput = Omit<WorkspaceConfig, 'sectionCopy' | 'heroMetricKeys' | 'blockOrder' | 'quickLinks' | 'briefingCards'> & {
|
||||
heroMetricKeys?: WorkspaceMetricKey[];
|
||||
blockOrder?: WorkspaceDetailBlockKey[];
|
||||
sectionCopy?: Partial<Record<WorkspaceSectionKey, WorkspaceSectionCopy>>;
|
||||
quickLinks?: WorkspaceQuickLink[];
|
||||
briefingCards?: WorkspaceBriefingCard[];
|
||||
};
|
||||
|
||||
const defaultBlockOrder: WorkspaceDetailBlockKey[] = [
|
||||
'focus',
|
||||
'summary',
|
||||
'watchlist',
|
||||
'approvalRisk',
|
||||
'operations',
|
||||
'delivery',
|
||||
'actions',
|
||||
];
|
||||
|
||||
const defaultSectionCopy: Record<WorkspaceSectionKey, WorkspaceSectionCopy> = {
|
||||
approvalQueue: {
|
||||
eyebrow: 'Approval inbox',
|
||||
title: 'Pending institutional decisions',
|
||||
actionLabel: 'Open approvals',
|
||||
},
|
||||
riskPanel: {
|
||||
eyebrow: 'Risk and red-flag panel',
|
||||
title: 'Compliance, audit, and payment exposure',
|
||||
},
|
||||
procurementQueue: {
|
||||
eyebrow: 'Procurement workflow',
|
||||
title: 'Live procurement pipeline',
|
||||
actionLabel: 'All requisitions',
|
||||
},
|
||||
contractWatchlist: {
|
||||
eyebrow: 'Contract watchlist',
|
||||
title: 'Expiring commitments',
|
||||
actionLabel: 'Contract register',
|
||||
},
|
||||
recentNotifications: {
|
||||
eyebrow: 'Notifications',
|
||||
title: 'Recent operational signals',
|
||||
actionLabel: 'Notification center',
|
||||
},
|
||||
provinceRollout: {
|
||||
eyebrow: 'Geographic rollout',
|
||||
title: 'Projects by province',
|
||||
actionLabel: 'Projects register',
|
||||
},
|
||||
topContracts: {
|
||||
eyebrow: 'Top-value contracts',
|
||||
title: 'Largest commitments currently tracked',
|
||||
actionLabel: 'Vendor master',
|
||||
},
|
||||
quickActions: {
|
||||
eyebrow: 'Action launcher',
|
||||
title: 'Move from oversight to execution',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultQuickLinks: WorkspaceQuickLink[] = [
|
||||
{
|
||||
href: '/requisitions/requisitions-new',
|
||||
label: 'Create requisition',
|
||||
description: 'Start the procurement workflow.',
|
||||
icon: 'requisitions',
|
||||
},
|
||||
{
|
||||
href: '/approvals/approvals-list',
|
||||
label: 'Approval inbox',
|
||||
description: 'Review pending decisions and escalations.',
|
||||
icon: 'approvals',
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Contract register',
|
||||
description: 'Inspect value, milestones, and expiry status.',
|
||||
icon: 'contracts',
|
||||
},
|
||||
{
|
||||
href: '/audit_logs/audit_logs-list',
|
||||
label: 'Audit center',
|
||||
description: 'Trace actions, timestamps, and record history.',
|
||||
icon: 'audit',
|
||||
},
|
||||
];
|
||||
|
||||
const createWorkspaceConfig = ({
|
||||
heroMetricKeys,
|
||||
blockOrder,
|
||||
sectionCopy,
|
||||
quickLinks,
|
||||
briefingCards,
|
||||
...config
|
||||
}: WorkspaceConfigInput): WorkspaceConfig => ({
|
||||
...config,
|
||||
heroMetricKeys: heroMetricKeys || config.highlightedMetricKeys.slice(0, 4),
|
||||
blockOrder: blockOrder || defaultBlockOrder,
|
||||
sectionCopy: {
|
||||
...defaultSectionCopy,
|
||||
...sectionCopy,
|
||||
},
|
||||
quickLinks: quickLinks || defaultQuickLinks,
|
||||
briefingCards: briefingCards || [],
|
||||
});
|
||||
|
||||
const workspaceConfigs: Record<string, WorkspaceConfig> = {
|
||||
[WORKSPACE_ROLES.superAdmin]: createWorkspaceConfig({
|
||||
sidebarLabel: 'Platform Administration',
|
||||
pageTitle: 'Platform Administration',
|
||||
eyebrow: 'FDSU ERP · Platform Administration',
|
||||
heroTitle: 'Govern tenants, access structure, and audit visibility across the platform.',
|
||||
heroDescription:
|
||||
'This workspace is for the Super Admin only. It focuses on organizations, platform users, role design, permission governance, and audit visibility across tenants without turning the Super Admin into an organization-level ERP operator.',
|
||||
primaryAction: { href: '/organizations/organizations-list', label: 'Review organizations' },
|
||||
secondaryAction: { href: '/permissions/permissions-list', label: 'Review permissions' },
|
||||
briefingCards: [
|
||||
{
|
||||
title: 'Should see',
|
||||
items: ['Organizations using the platform', 'Platform users, roles, permissions, and access assignments', 'Audit activity and cross-tenant control exceptions'],
|
||||
},
|
||||
{
|
||||
title: 'Should do',
|
||||
items: ['Create and govern organizations', 'Maintain the platform access model and segregation of duties', 'Support administrators without owning daily ERP transactions'],
|
||||
},
|
||||
{
|
||||
title: 'Receives from',
|
||||
items: ['Tenant setup requests', 'Access-governance escalations', 'Audit findings and platform support issues'],
|
||||
},
|
||||
{
|
||||
title: 'Hands off to',
|
||||
items: ['Administrators for organization execution', 'Functional leads for business workflows', 'Director General or tenant owners when decisions become operational'],
|
||||
},
|
||||
],
|
||||
highlightedMetricKeys: ['organizationCount', 'platformUserCount', 'roleCount', 'permissionCount', 'auditEventCount', 'unreadNotifications'],
|
||||
heroMetricKeys: ['organizationCount', 'platformUserCount', 'roleCount', 'auditEventCount'],
|
||||
blockOrder: ['summary', 'focus', 'actions'],
|
||||
sectionCopy: {
|
||||
quickActions: {
|
||||
eyebrow: 'Platform administration shortcuts',
|
||||
title: 'Go directly to organizations, access models, permissions, audit logs, and notices',
|
||||
},
|
||||
},
|
||||
quickLinks: [
|
||||
{
|
||||
href: '/organizations/organizations-list',
|
||||
label: 'Organizations',
|
||||
description: 'Review tenants, owners, and platform coverage.',
|
||||
icon: 'organizations',
|
||||
},
|
||||
{
|
||||
href: '/users/users-list',
|
||||
label: 'Platform users',
|
||||
description: 'Inspect who has access across the platform.',
|
||||
icon: 'users',
|
||||
},
|
||||
{
|
||||
href: '/roles/roles-list',
|
||||
label: 'Roles',
|
||||
description: 'Maintain responsibility boundaries and access structure.',
|
||||
icon: 'users',
|
||||
},
|
||||
{
|
||||
href: '/permissions/permissions-list',
|
||||
label: 'Permissions',
|
||||
description: 'Review the permission catalogue and control surface.',
|
||||
icon: 'audit',
|
||||
},
|
||||
{
|
||||
href: '/audit_logs/audit_logs-list',
|
||||
label: 'Audit logs',
|
||||
description: 'Trace platform activity and investigate access changes.',
|
||||
icon: 'audit',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[WORKSPACE_ROLES.administrator]: createWorkspaceConfig({
|
||||
sidebarLabel: 'Organization Administration',
|
||||
pageTitle: 'Organization Administration',
|
||||
eyebrow: 'FDSU ERP · Organization Administration',
|
||||
heroTitle: 'Keep the organization ready to operate: users, routing, notices, and master data.',
|
||||
heroDescription:
|
||||
'This workspace is for the Administrator. It owns user setup, departments, provinces, approval routing, notifications, and day-to-day system readiness. It should not read like a finance, procurement, or project command center.',
|
||||
primaryAction: { href: '/approval_workflows/approval_workflows-list', label: 'Review workflow setup' },
|
||||
secondaryAction: { href: '/notifications/notifications-list', label: 'Review notifications' },
|
||||
briefingCards: [
|
||||
{
|
||||
title: 'Should see',
|
||||
items: ['Workflow readiness and routing issues', 'Users, departments, provinces, and notices', 'Configuration gaps slowing daily operations'],
|
||||
},
|
||||
{
|
||||
title: 'Should do',
|
||||
items: ['Onboard and support users', 'Maintain approval steps, delegations, and notifications', 'Keep administrative records clean and usable'],
|
||||
},
|
||||
{
|
||||
title: 'Receives from',
|
||||
items: ['Super Admin policy and access structure', 'Support requests from staff', 'Broken-routing or setup escalations from functional leads'],
|
||||
},
|
||||
{
|
||||
title: 'Hands off to',
|
||||
items: ['Operational staff for daily execution', 'Functional leads for domain decisions', 'Director General when administrative blockages affect the institution'],
|
||||
},
|
||||
],
|
||||
highlightedMetricKeys: ['pendingApprovals', 'unreadNotifications', 'openRiskAlerts', 'vendorComplianceAlerts'],
|
||||
heroMetricKeys: ['pendingApprovals', 'unreadNotifications', 'openRiskAlerts', 'vendorComplianceAlerts'],
|
||||
blockOrder: ['summary', 'focus', 'watchlist', 'approvalRisk', 'actions'],
|
||||
sectionCopy: {
|
||||
approvalQueue: {
|
||||
eyebrow: 'Workflow routing queue',
|
||||
title: 'Approvals waiting because routing, delegation, or setup needs attention',
|
||||
actionLabel: 'Open approval queue',
|
||||
},
|
||||
riskPanel: {
|
||||
eyebrow: 'Administrative control signals',
|
||||
title: 'Alerts and exceptions that often point to setup, delegation, or record issues',
|
||||
},
|
||||
recentNotifications: {
|
||||
eyebrow: 'Administrator notices',
|
||||
title: 'Support and system notices requiring administrator follow-through',
|
||||
actionLabel: 'Open notices',
|
||||
},
|
||||
quickActions: {
|
||||
eyebrow: 'Administration shortcuts',
|
||||
title: 'Go straight to users, workflow setup, notifications, and organization records',
|
||||
},
|
||||
},
|
||||
quickLinks: [
|
||||
{
|
||||
href: '/users/users-list',
|
||||
label: 'Users',
|
||||
description: 'Manage onboarding, account support, and assignments.',
|
||||
icon: 'users',
|
||||
},
|
||||
{
|
||||
href: '/approval_workflows/approval_workflows-list',
|
||||
label: 'Approval workflows',
|
||||
description: 'Maintain routes, steps, and approval readiness.',
|
||||
icon: 'approvals',
|
||||
},
|
||||
{
|
||||
href: '/notifications/notifications-list',
|
||||
label: 'Notifications',
|
||||
description: 'Review notices, workflow signals, and support follow-up.',
|
||||
icon: 'notifications',
|
||||
},
|
||||
{
|
||||
href: '/departments/departments-list',
|
||||
label: 'Departments',
|
||||
description: 'Keep organization structure and master data aligned.',
|
||||
icon: 'users',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[WORKSPACE_ROLES.directorGeneral]: createWorkspaceConfig({
|
||||
sidebarLabel: 'Executive Oversight',
|
||||
pageTitle: 'Executive Oversight',
|
||||
eyebrow: 'FDSU ERP · Executive Oversight',
|
||||
heroTitle: 'See the institution across finance, procurement, compliance, and delivery, then make executive decisions.',
|
||||
heroDescription:
|
||||
'This workspace is for the Director General. It brings together budget posture, delivery progress, risk concentration, and high-impact approvals so executive attention stays on direction, escalation, and accountability.',
|
||||
primaryAction: { href: '/approvals/approvals-list', label: 'Review escalations' },
|
||||
secondaryAction: { href: '/projects/projects-list', label: 'Review project portfolio' },
|
||||
briefingCards: [
|
||||
{
|
||||
title: 'Should see',
|
||||
items: ['Strategic budget posture', 'Delivery momentum and risk concentration', 'Major approvals and contracts needing executive direction'],
|
||||
},
|
||||
{
|
||||
title: 'Should do',
|
||||
items: ['Approve high-impact decisions', 'Set priorities across functions', 'Resolve escalations that cross departmental boundaries'],
|
||||
},
|
||||
{
|
||||
title: 'Receives from',
|
||||
items: ['Finance, procurement, compliance, and delivery leads', 'Administrator escalations with institution-wide impact'],
|
||||
},
|
||||
{
|
||||
title: 'Hands off to',
|
||||
items: ['Functional leads for execution', 'Administrator for coordination follow-through', 'Operational teams through approved decisions'],
|
||||
},
|
||||
],
|
||||
highlightedMetricKeys: ['approvedBudget', 'committedBudget', 'disbursedBudget', 'pendingApprovals', 'highRiskProjects', 'contractsNearingExpiry'],
|
||||
heroMetricKeys: ['approvedBudget', 'disbursedBudget', 'pendingApprovals', 'highRiskProjects'],
|
||||
blockOrder: ['summary', 'focus', 'watchlist', 'delivery', 'approvalRisk', 'actions'],
|
||||
sectionCopy: {
|
||||
riskPanel: {
|
||||
eyebrow: 'Executive risk watch',
|
||||
title: 'Strategic risks, bottlenecks, and control exposure needing executive attention',
|
||||
},
|
||||
provinceRollout: {
|
||||
eyebrow: 'Delivery coverage',
|
||||
title: 'Project rollout and execution pace across provinces',
|
||||
actionLabel: 'Open projects',
|
||||
},
|
||||
topContracts: {
|
||||
eyebrow: 'Major commitments',
|
||||
title: 'Largest contracts shaping institutional exposure and delivery',
|
||||
actionLabel: 'Open contracts',
|
||||
},
|
||||
quickActions: {
|
||||
eyebrow: 'Executive shortcuts',
|
||||
title: 'Open the records most relevant to executive review and escalation',
|
||||
},
|
||||
},
|
||||
quickLinks: [
|
||||
{
|
||||
href: '/approvals/approvals-list',
|
||||
label: 'Escalations',
|
||||
description: 'Review approvals and issues that need executive decision.',
|
||||
icon: 'approvals',
|
||||
},
|
||||
{
|
||||
href: '/projects/projects-list',
|
||||
label: 'Projects',
|
||||
description: 'Review delivery status and accountability.',
|
||||
icon: 'projects',
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Contracts',
|
||||
description: 'Inspect major commitments and expiry exposure.',
|
||||
icon: 'contracts',
|
||||
},
|
||||
{
|
||||
href: '/notifications/notifications-list',
|
||||
label: 'Executive notices',
|
||||
description: 'See the latest escalations and control signals.',
|
||||
icon: 'notifications',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[WORKSPACE_ROLES.financeDirector]: createWorkspaceConfig({
|
||||
sidebarLabel: 'Financial Management',
|
||||
pageTitle: 'Financial Management',
|
||||
eyebrow: 'FDSU ERP · Financial Management',
|
||||
heroTitle: 'Control budget, commitments, disbursements, and payment pressure.',
|
||||
heroDescription:
|
||||
'This workspace is for the Finance Director. It focuses on allocations, commitments, disbursements, aging payment requests, and finance approvals while staying tied to the contracts and spending decisions that drive exposure.',
|
||||
primaryAction: { href: '/payment_requests/payment_requests-list', label: 'Review payment requests' },
|
||||
secondaryAction: { href: '/allocations/allocations-list', label: 'Review allocations' },
|
||||
briefingCards: [
|
||||
{
|
||||
title: 'Should see',
|
||||
items: ['Approved versus committed budget', 'Payment backlog and disbursement risk', 'Allocation pressure and finance approvals'],
|
||||
},
|
||||
{
|
||||
title: 'Should do',
|
||||
items: ['Validate funding coverage', 'Clear finance approvals and payment bottlenecks', 'Escalate shortfalls and fiscal-control issues'],
|
||||
},
|
||||
{
|
||||
title: 'Receives from',
|
||||
items: ['Operational finance staff', 'Procurement commitments and award decisions', 'Administrator-routed approvals'],
|
||||
},
|
||||
{
|
||||
title: 'Hands off to',
|
||||
items: ['Director General for major funding decisions', 'Payment and ledger staff for execution', 'Procurement and delivery leads on affordability constraints'],
|
||||
},
|
||||
],
|
||||
highlightedMetricKeys: ['approvedBudget', 'committedBudget', 'disbursedBudget', 'overduePayments', 'pendingApprovals', 'unreadNotifications'],
|
||||
heroMetricKeys: ['approvedBudget', 'committedBudget', 'disbursedBudget', 'overduePayments'],
|
||||
blockOrder: ['summary', 'focus', 'watchlist', 'approvalRisk', 'actions'],
|
||||
sectionCopy: {
|
||||
approvalQueue: {
|
||||
eyebrow: 'Finance approval queue',
|
||||
title: 'Approvals affecting commitments, disbursements, and payment timing',
|
||||
actionLabel: 'Open finance approvals',
|
||||
},
|
||||
riskPanel: {
|
||||
eyebrow: 'Fiscal exposure',
|
||||
title: 'Budget pressure, aging payments, and control exceptions affecting finance',
|
||||
},
|
||||
quickActions: {
|
||||
eyebrow: 'Finance shortcuts',
|
||||
title: 'Go directly to allocations, payment requests, commitments, and approvals',
|
||||
},
|
||||
},
|
||||
quickLinks: [
|
||||
{
|
||||
href: '/payment_requests/payment_requests-list',
|
||||
label: 'Payment requests',
|
||||
description: 'Review pending and aging disbursement requests.',
|
||||
icon: 'payments',
|
||||
},
|
||||
{
|
||||
href: '/allocations/allocations-list',
|
||||
label: 'Allocations',
|
||||
description: 'Check available headroom and funding coverage.',
|
||||
icon: 'allocations',
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Commitments',
|
||||
description: 'Inspect the contracts driving current spend exposure.',
|
||||
icon: 'contracts',
|
||||
},
|
||||
{
|
||||
href: '/approvals/approvals-list',
|
||||
label: 'Finance approvals',
|
||||
description: 'Track bottlenecks holding back finance action.',
|
||||
icon: 'approvals',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[WORKSPACE_ROLES.procurementLead]: createWorkspaceConfig({
|
||||
sidebarLabel: 'Procurement Management',
|
||||
pageTitle: 'Procurement Management',
|
||||
eyebrow: 'FDSU ERP · Procurement Management',
|
||||
heroTitle: 'Own the demand-to-award chain: requisitions, tenders, vendors, awards, and contract readiness.',
|
||||
heroDescription:
|
||||
'This workspace is for the Procurement Lead. It keeps sourcing leadership focused on requisition inflow, tender progression, vendor readiness, contract deadlines, and approvals that are slowing the procurement chain.',
|
||||
primaryAction: { href: '/requisitions/requisitions-list', label: 'Review requisitions' },
|
||||
secondaryAction: { href: '/tenders/tenders-list', label: 'Review tenders' },
|
||||
briefingCards: [
|
||||
{
|
||||
title: 'Should see',
|
||||
items: ['Requisition inflow and tender progress', 'Expiring contracts and supplier readiness', 'Approvals slowing sourcing and award decisions'],
|
||||
},
|
||||
{
|
||||
title: 'Should do',
|
||||
items: ['Move demand into sourcing', 'Review awards and vendor readiness', 'Escalate blockers in the procurement chain'],
|
||||
},
|
||||
{
|
||||
title: 'Receives from',
|
||||
items: ['Operational requesters and departments', 'Project teams needing sourcing support', 'Administrator workflow routing and setup support'],
|
||||
},
|
||||
{
|
||||
title: 'Hands off to',
|
||||
items: ['Finance on commitments and funding coverage', 'Compliance on control concerns', 'Director General on high-value or escalated decisions'],
|
||||
},
|
||||
],
|
||||
highlightedMetricKeys: ['procurementPipeline', 'pendingApprovals', 'contractsNearingExpiry', 'vendorComplianceAlerts', 'openRiskAlerts', 'activeProjects'],
|
||||
heroMetricKeys: ['procurementPipeline', 'pendingApprovals', 'contractsNearingExpiry', 'vendorComplianceAlerts'],
|
||||
blockOrder: ['summary', 'focus', 'watchlist', 'approvalRisk', 'operations', 'actions'],
|
||||
sectionCopy: {
|
||||
approvalQueue: {
|
||||
eyebrow: 'Procurement approval queue',
|
||||
title: 'Decisions blocking sourcing, evaluation, award, or contract signature',
|
||||
actionLabel: 'Open approval backlog',
|
||||
},
|
||||
procurementQueue: {
|
||||
eyebrow: 'Sourcing pipeline',
|
||||
title: 'Requisitions and tenders currently needing procurement action',
|
||||
actionLabel: 'Open sourcing queue',
|
||||
},
|
||||
contractWatchlist: {
|
||||
eyebrow: 'Contract renewals',
|
||||
title: 'Supplier commitments nearing expiry or needing follow-through',
|
||||
actionLabel: 'Open contracts',
|
||||
},
|
||||
quickActions: {
|
||||
eyebrow: 'Procurement shortcuts',
|
||||
title: 'Go directly to requisitions, tenders, vendors, and contract follow-up',
|
||||
},
|
||||
},
|
||||
quickLinks: [
|
||||
{
|
||||
href: '/requisitions/requisitions-list',
|
||||
label: 'Requisitions',
|
||||
description: 'Review new demand and sourcing readiness.',
|
||||
icon: 'requisitions',
|
||||
},
|
||||
{
|
||||
href: '/tenders/tenders-list',
|
||||
label: 'Tenders',
|
||||
description: 'Follow active tenders and consultations.',
|
||||
icon: 'tenders',
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Contracts',
|
||||
description: 'Track expiring commitments and follow-up actions.',
|
||||
icon: 'contracts',
|
||||
},
|
||||
{
|
||||
href: '/vendors/vendors-list',
|
||||
label: 'Vendors',
|
||||
description: 'Inspect supplier readiness and history.',
|
||||
icon: 'vendors',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[WORKSPACE_ROLES.complianceAuditLead]: createWorkspaceConfig({
|
||||
sidebarLabel: 'Compliance & Audit',
|
||||
pageTitle: 'Compliance & Audit',
|
||||
eyebrow: 'FDSU ERP · Compliance & Audit',
|
||||
heroTitle: 'Watch control breaches, evidence gaps, expiring obligations, and approvals that need independent review.',
|
||||
heroDescription:
|
||||
'This workspace is for the Compliance and Audit Lead. It focuses on red flags, evidence quality, audit trails, expiring obligations, and approvals where controls must be checked before risk becomes failure.',
|
||||
primaryAction: { href: '/compliance_alerts/compliance_alerts-list', label: 'Review compliance alerts' },
|
||||
secondaryAction: { href: '/audit_logs/audit_logs-list', label: 'Review audit logs' },
|
||||
briefingCards: [
|
||||
{
|
||||
title: 'Should see',
|
||||
items: ['Compliance alerts and evidence gaps', 'Expiring obligations and aging items', 'Approvals requiring control review'],
|
||||
},
|
||||
{
|
||||
title: 'Should do',
|
||||
items: ['Investigate red flags', 'Trace actions in audit logs', 'Push corrective action and evidence follow-up'],
|
||||
},
|
||||
{
|
||||
title: 'Receives from',
|
||||
items: ['System alerts and audit trails', 'Functional leads reporting control exceptions', 'Administrator notices and escalations'],
|
||||
},
|
||||
{
|
||||
title: 'Hands off to',
|
||||
items: ['Director General on serious exposure', 'Process owners for remediation', 'Staff for evidence completion and follow-up'],
|
||||
},
|
||||
],
|
||||
highlightedMetricKeys: ['openRiskAlerts', 'vendorComplianceAlerts', 'contractsNearingExpiry', 'pendingApprovals', 'overduePayments', 'unreadNotifications'],
|
||||
heroMetricKeys: ['openRiskAlerts', 'vendorComplianceAlerts', 'contractsNearingExpiry', 'pendingApprovals'],
|
||||
blockOrder: ['summary', 'focus', 'watchlist', 'approvalRisk', 'actions'],
|
||||
sectionCopy: {
|
||||
approvalQueue: {
|
||||
eyebrow: 'Control review queue',
|
||||
title: 'Approvals and cases requiring audit or compliance follow-through',
|
||||
actionLabel: 'Open control queue',
|
||||
},
|
||||
riskPanel: {
|
||||
eyebrow: 'Compliance watch',
|
||||
title: 'Exceptions, evidence gaps, overdue items, and red flags needing review',
|
||||
},
|
||||
recentNotifications: {
|
||||
eyebrow: 'Control notices',
|
||||
title: 'Recent signals that may indicate policy, evidence, or routing problems',
|
||||
actionLabel: 'Open notices',
|
||||
},
|
||||
quickActions: {
|
||||
eyebrow: 'Compliance shortcuts',
|
||||
title: 'Go directly to alerts, logs, approvals, and obligations needing review',
|
||||
},
|
||||
},
|
||||
quickLinks: [
|
||||
{
|
||||
href: '/compliance_alerts/compliance_alerts-list',
|
||||
label: 'Compliance alerts',
|
||||
description: 'Review open red flags and follow-up status.',
|
||||
icon: 'compliance',
|
||||
},
|
||||
{
|
||||
href: '/audit_logs/audit_logs-list',
|
||||
label: 'Audit logs',
|
||||
description: 'Trace evidence across users and records.',
|
||||
icon: 'audit',
|
||||
},
|
||||
{
|
||||
href: '/approvals/approvals-list',
|
||||
label: 'Control queue',
|
||||
description: 'Inspect approvals needing governance review.',
|
||||
icon: 'approvals',
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Obligations',
|
||||
description: 'Check commitments nearing expiry or breach.',
|
||||
icon: 'contracts',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[WORKSPACE_ROLES.projectDeliveryLead]: createWorkspaceConfig({
|
||||
sidebarLabel: 'Project Delivery',
|
||||
pageTitle: 'Project Delivery',
|
||||
eyebrow: 'FDSU ERP · Project Delivery',
|
||||
heroTitle: 'Keep projects moving by watching milestones, execution risk, approvals, and payment delays.',
|
||||
heroDescription:
|
||||
'This workspace is for the Project Delivery Lead. It centers on implementation progress, field issues, delivery blockers, and the finance or procurement dependencies that can delay execution.',
|
||||
primaryAction: { href: '/projects/projects-list', label: 'Review projects' },
|
||||
secondaryAction: { href: '/project_milestones/project_milestones-list', label: 'Review milestones' },
|
||||
briefingCards: [
|
||||
{
|
||||
title: 'Should see',
|
||||
items: ['Project status and milestone slippage', 'Field issues and execution risks', 'Approvals or payments delaying delivery'],
|
||||
},
|
||||
{
|
||||
title: 'Should do',
|
||||
items: ['Review project progress', 'Clear delivery blockers with other leads', 'Escalate risks before timelines fail'],
|
||||
},
|
||||
{
|
||||
title: 'Receives from',
|
||||
items: ['Project staff and field verification teams', 'Procurement and finance status updates', 'Administrator workflow escalations'],
|
||||
},
|
||||
{
|
||||
title: 'Hands off to',
|
||||
items: ['Director General for major decisions', 'Procurement and finance leads on blockers', 'Operational project teams for execution'],
|
||||
},
|
||||
],
|
||||
highlightedMetricKeys: ['activeProjects', 'averageProjectProgress', 'highRiskProjects', 'pendingApprovals', 'overduePayments', 'unreadNotifications'],
|
||||
heroMetricKeys: ['activeProjects', 'averageProjectProgress', 'highRiskProjects', 'pendingApprovals'],
|
||||
blockOrder: ['summary', 'focus', 'watchlist', 'delivery', 'approvalRisk', 'actions'],
|
||||
sectionCopy: {
|
||||
approvalQueue: {
|
||||
eyebrow: 'Delivery blocker queue',
|
||||
title: 'Approvals holding back project execution',
|
||||
actionLabel: 'Open delivery blockers',
|
||||
},
|
||||
provinceRollout: {
|
||||
eyebrow: 'Delivery footprint',
|
||||
title: 'Project progress and execution pace by province',
|
||||
actionLabel: 'Open project register',
|
||||
},
|
||||
topContracts: {
|
||||
eyebrow: 'Delivery contracts',
|
||||
title: 'Largest contracts underpinning execution and milestone delivery',
|
||||
actionLabel: 'Open contracts',
|
||||
},
|
||||
quickActions: {
|
||||
eyebrow: 'Delivery shortcuts',
|
||||
title: 'Go directly to projects, milestones, blockers, and supporting contracts',
|
||||
},
|
||||
},
|
||||
quickLinks: [
|
||||
{
|
||||
href: '/projects/projects-list',
|
||||
label: 'Projects',
|
||||
description: 'Review implementation status and ownership.',
|
||||
icon: 'projects',
|
||||
},
|
||||
{
|
||||
href: '/project_milestones/project_milestones-list',
|
||||
label: 'Milestones',
|
||||
description: 'Track critical delivery checkpoints.',
|
||||
icon: 'milestones',
|
||||
},
|
||||
{
|
||||
href: '/approvals/approvals-list',
|
||||
label: 'Delivery blockers',
|
||||
description: 'See approvals slowing field execution.',
|
||||
icon: 'approvals',
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Supporting contracts',
|
||||
description: 'Inspect contracts affecting delivery scope.',
|
||||
icon: 'contracts',
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
|
||||
export function getWorkspaceConfig(roleName?: string | null): WorkspaceConfig {
|
||||
if (!roleName) {
|
||||
return workspaceConfigs[WORKSPACE_ROLES.projectDeliveryLead];
|
||||
}
|
||||
|
||||
return workspaceConfigs[roleName] || workspaceConfigs[WORKSPACE_ROLES.projectDeliveryLead];
|
||||
}
|
||||
|
||||
export function getWorkspaceRoute(roleName?: string | null) {
|
||||
if (!roleName) {
|
||||
return '/executive-summary';
|
||||
}
|
||||
|
||||
return WORKSPACE_ROUTES[roleName] || '/executive-summary';
|
||||
}
|
||||
|
||||
export function normalizeInternalRedirectTarget(target?: string | string[] | null) {
|
||||
const redirectTarget = Array.isArray(target) ? target[0] : target;
|
||||
|
||||
if (!redirectTarget || typeof redirectTarget !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!redirectTarget.startsWith('/') || redirectTarget.startsWith('//')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (redirectTarget.startsWith('/login')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return redirectTarget;
|
||||
}
|
||||
|
||||
export function getPostLoginRoute(roleName?: string | null, redirectTarget?: string | string[] | null) {
|
||||
return normalizeInternalRedirectTarget(redirectTarget) || getWorkspaceRoute(roleName);
|
||||
}
|
||||
|
||||
export function getLoginRoute(redirectTarget?: string | string[] | null) {
|
||||
const safeRedirectTarget = normalizeInternalRedirectTarget(redirectTarget);
|
||||
|
||||
if (!safeRedirectTarget) {
|
||||
return '/login';
|
||||
}
|
||||
|
||||
return `/login?redirect=${encodeURIComponent(safeRedirectTarget)}`;
|
||||
}
|
||||
|
||||
export function isDashboardRole(roleName?: string | null) {
|
||||
if (!roleName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ADMIN_DASHBOARD_ROLES.includes(roleName as (typeof ADMIN_DASHBOARD_ROLES)[number]);
|
||||
}
|
||||
|
||||
export function itemVisibleForRole(itemRoles?: string[], roleName?: string | null) {
|
||||
if (!itemRoles?.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!roleName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return itemRoles.includes(roleName);
|
||||
}
|
||||
@ -14,8 +14,6 @@ export type MenuAsideItem = {
|
||||
withDevider?: boolean;
|
||||
menu?: MenuAsideItem[]
|
||||
permissions?: string | string[]
|
||||
roles?: string[]
|
||||
labelByRole?: Record<string, string>
|
||||
}
|
||||
|
||||
export type MenuNavBarItem = {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { ReactNode, useEffect, useState } from 'react'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { mdiBackburger, mdiForwardburger, mdiMenu } from '@mdi/js'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { ReactNode, useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
||||
import menuAside from '../menuAside'
|
||||
import menuNavBar from '../menuNavBar'
|
||||
import BaseIcon from '../components/BaseIcon'
|
||||
@ -9,60 +9,66 @@ import NavBar from '../components/NavBar'
|
||||
import NavBarItemPlain from '../components/NavBarItemPlain'
|
||||
import AsideMenu from '../components/AsideMenu'
|
||||
import FooterBar from '../components/FooterBar'
|
||||
import Search from '../components/Search'
|
||||
import { useAppDispatch, useAppSelector } from '../stores/hooks'
|
||||
import { findMe, logoutUser } from '../stores/authSlice'
|
||||
import { hasPermission } from '../helpers/userPermissions'
|
||||
import { getLoginRoute } from '../helpers/workspace'
|
||||
import Search from '../components/Search';
|
||||
import { useRouter } from 'next/router'
|
||||
import {findMe, logoutUser} from "../stores/authSlice";
|
||||
|
||||
import {hasPermission} from "../helpers/userPermissions";
|
||||
|
||||
|
||||
type Props = {
|
||||
children: ReactNode
|
||||
|
||||
permission?: string
|
||||
|
||||
}
|
||||
|
||||
export default function LayoutAuthenticated({ children, permission }: Props) {
|
||||
export default function LayoutAuthenticated({
|
||||
children,
|
||||
|
||||
permission
|
||||
|
||||
}: Props) {
|
||||
const dispatch = useAppDispatch()
|
||||
const router = useRouter()
|
||||
const { token, currentUser } = useAppSelector((state) => state.auth)
|
||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor)
|
||||
const darkMode = useAppSelector((state) => state.style.darkMode)
|
||||
|
||||
const [isAsideMobileExpanded, setIsAsideMobileExpanded] = useState(false)
|
||||
const [isAsideLgActive, setIsAsideLgActive] = useState(false)
|
||||
|
||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||
let localToken
|
||||
if (typeof window !== 'undefined') {
|
||||
// Perform localStorage action
|
||||
localToken = localStorage.getItem('token')
|
||||
}
|
||||
|
||||
const isTokenValid = () => {
|
||||
const storedToken = localStorage.getItem('token')
|
||||
|
||||
if (!storedToken) return
|
||||
|
||||
const date = new Date().getTime() / 1000
|
||||
const data: any = jwt.decode(storedToken)
|
||||
|
||||
if (!data) return
|
||||
|
||||
return date < data.exp
|
||||
}
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const date = new Date().getTime() / 1000;
|
||||
const data = jwt.decode(token);
|
||||
if (!data) return;
|
||||
return date < data.exp;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(findMe());
|
||||
if (!isTokenValid()) {
|
||||
dispatch(logoutUser())
|
||||
router.replace(getLoginRoute(router.asPath))
|
||||
return
|
||||
dispatch(logoutUser());
|
||||
router.push('/login');
|
||||
}
|
||||
}, [token, localToken]);
|
||||
|
||||
dispatch(findMe())
|
||||
}, [dispatch, localToken, router, token])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!permission || !currentUser) return
|
||||
if (!permission || !currentUser) return;
|
||||
|
||||
if (!hasPermission(currentUser, permission)) router.push('/error')
|
||||
}, [currentUser, permission, router])
|
||||
if (!hasPermission(currentUser, permission)) router.push('/error');
|
||||
}, [currentUser, permission]);
|
||||
|
||||
|
||||
const darkMode = useAppSelector((state) => state.style.darkMode)
|
||||
|
||||
const [isAsideMobileExpanded, setIsAsideMobileExpanded] = useState(false)
|
||||
const [isAsideLgActive, setIsAsideLgActive] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleRouteChangeStart = () => {
|
||||
@ -72,51 +78,51 @@ export default function LayoutAuthenticated({ children, permission }: Props) {
|
||||
|
||||
router.events.on('routeChangeStart', handleRouteChangeStart)
|
||||
|
||||
// If the component is unmounted, unsubscribe
|
||||
// from the event with the `off` method:
|
||||
return () => {
|
||||
router.events.off('routeChangeStart', handleRouteChangeStart)
|
||||
}
|
||||
}, [router.events])
|
||||
}, [router.events, dispatch])
|
||||
|
||||
const layoutAsidePadding = 'xl:pl-72'
|
||||
|
||||
const layoutAsidePadding = 'xl:pl-60'
|
||||
|
||||
return (
|
||||
<div className={`${darkMode ? 'dark' : ''} overflow-hidden lg:overflow-visible`}>
|
||||
<div
|
||||
className={`${layoutAsidePadding} ${
|
||||
isAsideMobileExpanded ? 'ml-72 lg:ml-0' : ''
|
||||
} min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
|
||||
isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''
|
||||
} pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
|
||||
>
|
||||
<NavBar
|
||||
menu={menuNavBar}
|
||||
className={`${layoutAsidePadding} ${isAsideMobileExpanded ? 'ml-72 lg:ml-0' : ''}`}
|
||||
className={`${layoutAsidePadding} ${isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''}`}
|
||||
>
|
||||
<NavBarItemPlain
|
||||
display='flex lg:hidden'
|
||||
display="flex lg:hidden"
|
||||
onClick={() => setIsAsideMobileExpanded(!isAsideMobileExpanded)}
|
||||
>
|
||||
<BaseIcon path={isAsideMobileExpanded ? mdiBackburger : mdiForwardburger} size='24' />
|
||||
<BaseIcon path={isAsideMobileExpanded ? mdiBackburger : mdiForwardburger} size="24" />
|
||||
</NavBarItemPlain>
|
||||
<NavBarItemPlain display='hidden lg:flex xl:hidden' onClick={() => setIsAsideLgActive(true)}>
|
||||
<BaseIcon path={mdiMenu} size='24' />
|
||||
<NavBarItemPlain
|
||||
display="hidden lg:flex xl:hidden"
|
||||
onClick={() => setIsAsideLgActive(true)}
|
||||
>
|
||||
<BaseIcon path={mdiMenu} size="24" />
|
||||
</NavBarItemPlain>
|
||||
<NavBarItemPlain useMargin>
|
||||
<Search />
|
||||
</NavBarItemPlain>
|
||||
</NavBar>
|
||||
|
||||
<AsideMenu
|
||||
isAsideMobileExpanded={isAsideMobileExpanded}
|
||||
isAsideLgActive={isAsideLgActive}
|
||||
menu={menuAside}
|
||||
onAsideLgClose={() => setIsAsideLgActive(false)}
|
||||
/>
|
||||
|
||||
<div className='pt-14'>
|
||||
<div className='min-h-[calc(100vh-3.5rem)]'>
|
||||
{children}
|
||||
</div>
|
||||
<FooterBar />
|
||||
</div>
|
||||
{children}
|
||||
<FooterBar>Hand-crafted & Made with ❤️</FooterBar>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,295 +1,418 @@
|
||||
import * as icon from '@mdi/js'
|
||||
import * as icon from '@mdi/js';
|
||||
import { MenuAsideItem } from './interfaces'
|
||||
import { WORKSPACE_ROLES } from './helpers/workspace'
|
||||
|
||||
const optionalIcon = (name: string, fallback: string = icon.mdiTable): string => {
|
||||
const iconSet = icon as Record<string, string>
|
||||
|
||||
return iconSet[name] || fallback
|
||||
}
|
||||
|
||||
const superAdminWorkspaceRoles = [WORKSPACE_ROLES.superAdmin]
|
||||
const adminWorkspaceRoles = [WORKSPACE_ROLES.administrator]
|
||||
|
||||
const superAdminGroupedNavigation: MenuAsideItem[] = [
|
||||
{
|
||||
label: 'Platform Governance',
|
||||
icon: icon.mdiShieldAccountOutline,
|
||||
roles: superAdminWorkspaceRoles,
|
||||
withDevider: true,
|
||||
menu: [
|
||||
{
|
||||
href: '/organizations/organizations-list',
|
||||
label: 'Organizations',
|
||||
icon: optionalIcon('mdiDomain'),
|
||||
permissions: 'READ_ORGANIZATIONS',
|
||||
},
|
||||
{
|
||||
href: '/users/users-list',
|
||||
label: 'Users',
|
||||
icon: icon.mdiAccountGroup,
|
||||
permissions: 'READ_USERS',
|
||||
},
|
||||
{
|
||||
href: '/roles/roles-list',
|
||||
label: 'Roles',
|
||||
icon: optionalIcon('mdiShieldAccountVariantOutline'),
|
||||
permissions: 'READ_ROLES',
|
||||
},
|
||||
{
|
||||
href: '/permissions/permissions-list',
|
||||
label: 'Permissions',
|
||||
icon: optionalIcon('mdiShieldAccountOutline'),
|
||||
permissions: 'READ_PERMISSIONS',
|
||||
},
|
||||
{
|
||||
href: '/role_permissions/role_permissions-list',
|
||||
label: 'Role permissions',
|
||||
icon: optionalIcon('mdiLinkVariant'),
|
||||
permissions: 'READ_ROLE_PERMISSIONS',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Workflow & Oversight',
|
||||
icon: icon.mdiSitemap,
|
||||
roles: superAdminWorkspaceRoles,
|
||||
menu: [
|
||||
{
|
||||
href: '/approval_workflows/approval_workflows-list',
|
||||
label: 'Approval workflows',
|
||||
icon: optionalIcon('mdiSitemap'),
|
||||
permissions: 'READ_APPROVAL_WORKFLOWS',
|
||||
},
|
||||
{
|
||||
href: '/approval_steps/approval_steps-list',
|
||||
label: 'Approval steps',
|
||||
icon: optionalIcon('mdiStairs'),
|
||||
permissions: 'READ_APPROVAL_STEPS',
|
||||
},
|
||||
{
|
||||
href: '/notifications/notifications-list',
|
||||
label: 'Notifications',
|
||||
icon: optionalIcon('mdiBell'),
|
||||
permissions: 'READ_NOTIFICATIONS',
|
||||
},
|
||||
{
|
||||
href: '/audit_logs/audit_logs-list',
|
||||
label: 'Audit logs',
|
||||
icon: optionalIcon('mdiClipboardTextClock'),
|
||||
permissions: 'READ_AUDIT_LOGS',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const adminGroupedNavigation: MenuAsideItem[] = [
|
||||
{
|
||||
label: 'Organization Setup',
|
||||
icon: icon.mdiAccountGroup,
|
||||
roles: adminWorkspaceRoles,
|
||||
withDevider: true,
|
||||
menu: [
|
||||
{
|
||||
href: '/users/users-list',
|
||||
label: 'Users',
|
||||
icon: icon.mdiAccountGroup,
|
||||
permissions: 'READ_USERS',
|
||||
},
|
||||
{
|
||||
href: '/provinces/provinces-list',
|
||||
label: 'Provinces',
|
||||
icon: optionalIcon('mdiMapMarker'),
|
||||
permissions: 'READ_PROVINCES',
|
||||
},
|
||||
{
|
||||
href: '/departments/departments-list',
|
||||
label: 'Departments',
|
||||
icon: optionalIcon('mdiOfficeBuilding'),
|
||||
permissions: 'READ_DEPARTMENTS',
|
||||
},
|
||||
{
|
||||
href: '/documents/documents-list',
|
||||
label: 'Documents',
|
||||
icon: optionalIcon('mdiFolderFile'),
|
||||
permissions: 'READ_DOCUMENTS',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Workflow Control',
|
||||
icon: icon.mdiSitemap,
|
||||
roles: adminWorkspaceRoles,
|
||||
menu: [
|
||||
{
|
||||
href: '/approval_workflows/approval_workflows-list',
|
||||
label: 'Approval workflows',
|
||||
icon: optionalIcon('mdiSitemap'),
|
||||
permissions: 'READ_APPROVAL_WORKFLOWS',
|
||||
},
|
||||
{
|
||||
href: '/approval_steps/approval_steps-list',
|
||||
label: 'Approval steps',
|
||||
icon: optionalIcon('mdiStairs'),
|
||||
permissions: 'READ_APPROVAL_STEPS',
|
||||
},
|
||||
{
|
||||
href: '/approvals/approvals-list',
|
||||
label: 'Approvals',
|
||||
icon: optionalIcon('mdiChecklist'),
|
||||
permissions: 'READ_APPROVALS',
|
||||
},
|
||||
{
|
||||
href: '/role_permissions/role_permissions-list',
|
||||
label: 'Role access',
|
||||
icon: optionalIcon('mdiLinkVariant'),
|
||||
permissions: 'READ_ROLE_PERMISSIONS',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Assurance & Notices',
|
||||
icon: icon.mdiBellOutline,
|
||||
roles: adminWorkspaceRoles,
|
||||
menu: [
|
||||
{
|
||||
href: '/notifications/notifications-list',
|
||||
label: 'Notifications',
|
||||
icon: optionalIcon('mdiBell'),
|
||||
permissions: 'READ_NOTIFICATIONS',
|
||||
},
|
||||
{
|
||||
href: '/compliance_alerts/compliance_alerts-list',
|
||||
label: 'Compliance alerts',
|
||||
icon: optionalIcon('mdiShieldAlert'),
|
||||
permissions: 'READ_COMPLIANCE_ALERTS',
|
||||
},
|
||||
{
|
||||
href: '/audit_logs/audit_logs-list',
|
||||
label: 'Audit logs',
|
||||
icon: optionalIcon('mdiClipboardTextClock'),
|
||||
permissions: 'READ_AUDIT_LOGS',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const sharedEntityNavigation: MenuAsideItem[] = []
|
||||
|
||||
const menuAside: MenuAsideItem[] = [
|
||||
{
|
||||
href: '/executive-summary',
|
||||
icon: icon.mdiBankOutline,
|
||||
label: 'Role Workspace',
|
||||
labelByRole: {
|
||||
[WORKSPACE_ROLES.superAdmin]: 'Platform Administration',
|
||||
[WORKSPACE_ROLES.administrator]: 'Organization Administration',
|
||||
[WORKSPACE_ROLES.directorGeneral]: 'Executive Oversight',
|
||||
[WORKSPACE_ROLES.financeDirector]: 'Financial Management',
|
||||
[WORKSPACE_ROLES.procurementLead]: 'Procurement Management',
|
||||
[WORKSPACE_ROLES.complianceAuditLead]: 'Compliance & Audit',
|
||||
[WORKSPACE_ROLES.projectDeliveryLead]: 'Project Delivery',
|
||||
},
|
||||
},
|
||||
{
|
||||
href: '/dashboard',
|
||||
icon: icon.mdiViewDashboardOutline,
|
||||
label: 'Role Widgets',
|
||||
labelByRole: {
|
||||
[WORKSPACE_ROLES.superAdmin]: 'Platform Widgets',
|
||||
[WORKSPACE_ROLES.administrator]: 'Operations Widgets',
|
||||
},
|
||||
roles: [WORKSPACE_ROLES.superAdmin, WORKSPACE_ROLES.administrator],
|
||||
label: 'Dashboard',
|
||||
},
|
||||
...superAdminGroupedNavigation,
|
||||
...adminGroupedNavigation,
|
||||
|
||||
{
|
||||
href: '/projects/projects-list',
|
||||
label: 'Strategic Portfolio',
|
||||
icon: icon.mdiChartTimelineVariant,
|
||||
permissions: 'READ_PROJECTS',
|
||||
roles: [WORKSPACE_ROLES.directorGeneral],
|
||||
withDevider: true,
|
||||
href: '/users/users-list',
|
||||
label: 'Users',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: icon.mdiAccountGroup ?? icon.mdiTable,
|
||||
permissions: 'READ_USERS'
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Contract Exposure',
|
||||
icon: icon.mdiFileDocumentOutline,
|
||||
permissions: 'READ_CONTRACTS',
|
||||
roles: [WORKSPACE_ROLES.directorGeneral],
|
||||
href: '/roles/roles-list',
|
||||
label: 'Roles',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable,
|
||||
permissions: 'READ_ROLES'
|
||||
},
|
||||
{
|
||||
href: '/payment_requests/payment_requests-list',
|
||||
label: 'Payment Control',
|
||||
icon: icon.mdiCashCheck,
|
||||
permissions: 'READ_PAYMENT_REQUESTS',
|
||||
roles: [WORKSPACE_ROLES.financeDirector],
|
||||
withDevider: true,
|
||||
href: '/permissions/permissions-list',
|
||||
label: 'Permissions',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: icon.mdiShieldAccountOutline ?? icon.mdiTable,
|
||||
permissions: 'READ_PERMISSIONS'
|
||||
},
|
||||
{
|
||||
href: '/allocations/allocations-list',
|
||||
label: 'Allocation Register',
|
||||
icon: icon.mdiWalletOutline,
|
||||
permissions: 'READ_ALLOCATIONS',
|
||||
roles: [WORKSPACE_ROLES.financeDirector],
|
||||
href: '/organizations/organizations-list',
|
||||
label: 'Organizations',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_ORGANIZATIONS'
|
||||
},
|
||||
{
|
||||
href: '/requisitions/requisitions-list',
|
||||
label: 'Requisition Queue',
|
||||
icon: icon.mdiClipboardListOutline,
|
||||
permissions: 'READ_REQUISITIONS',
|
||||
roles: [WORKSPACE_ROLES.procurementLead],
|
||||
withDevider: true,
|
||||
href: '/provinces/provinces-list',
|
||||
label: 'Provinces',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiMapMarker' in icon ? icon['mdiMapMarker' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PROVINCES'
|
||||
},
|
||||
{
|
||||
href: '/tenders/tenders-list',
|
||||
label: 'Tender Pipeline',
|
||||
icon: icon.mdiGavel,
|
||||
permissions: 'READ_TENDERS',
|
||||
roles: [WORKSPACE_ROLES.procurementLead],
|
||||
href: '/departments/departments-list',
|
||||
label: 'Departments',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiOfficeBuilding' in icon ? icon['mdiOfficeBuilding' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_DEPARTMENTS'
|
||||
},
|
||||
{
|
||||
href: '/compliance_alerts/compliance_alerts-list',
|
||||
label: 'Control Exceptions',
|
||||
icon: icon.mdiShieldAlertOutline,
|
||||
permissions: 'READ_COMPLIANCE_ALERTS',
|
||||
roles: [WORKSPACE_ROLES.complianceAuditLead],
|
||||
withDevider: true,
|
||||
href: '/role_permissions/role_permissions-list',
|
||||
label: 'Role permissions',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiLinkVariant' in icon ? icon['mdiLinkVariant' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_ROLE_PERMISSIONS'
|
||||
},
|
||||
{
|
||||
href: '/approval_workflows/approval_workflows-list',
|
||||
label: 'Approval workflows',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiSitemap' in icon ? icon['mdiSitemap' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_APPROVAL_WORKFLOWS'
|
||||
},
|
||||
{
|
||||
href: '/approval_steps/approval_steps-list',
|
||||
label: 'Approval steps',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiStairs' in icon ? icon['mdiStairs' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_APPROVAL_STEPS'
|
||||
},
|
||||
{
|
||||
href: '/approvals/approvals-list',
|
||||
label: 'Approvals',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiChecklist' in icon ? icon['mdiChecklist' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_APPROVALS'
|
||||
},
|
||||
{
|
||||
href: '/notifications/notifications-list',
|
||||
label: 'Notifications',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiBell' in icon ? icon['mdiBell' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_NOTIFICATIONS'
|
||||
},
|
||||
{
|
||||
href: '/audit_logs/audit_logs-list',
|
||||
label: 'Audit Trail',
|
||||
icon: icon.mdiClipboardTextClock,
|
||||
permissions: 'READ_AUDIT_LOGS',
|
||||
roles: [WORKSPACE_ROLES.complianceAuditLead],
|
||||
label: 'Audit logs',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiClipboardTextClock' in icon ? icon['mdiClipboardTextClock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_AUDIT_LOGS'
|
||||
},
|
||||
{
|
||||
href: '/fiscal_years/fiscal_years-list',
|
||||
label: 'Fiscal years',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiCalendarRange' in icon ? icon['mdiCalendarRange' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_FISCAL_YEARS'
|
||||
},
|
||||
{
|
||||
href: '/funding_sources/funding_sources-list',
|
||||
label: 'Funding sources',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiCashMultiple' in icon ? icon['mdiCashMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_FUNDING_SOURCES'
|
||||
},
|
||||
{
|
||||
href: '/budget_programs/budget_programs-list',
|
||||
label: 'Budget programs',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiChartDonut' in icon ? icon['mdiChartDonut' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_BUDGET_PROGRAMS'
|
||||
},
|
||||
{
|
||||
href: '/budget_lines/budget_lines-list',
|
||||
label: 'Budget lines',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFormatListBulleted' in icon ? icon['mdiFormatListBulleted' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_BUDGET_LINES'
|
||||
},
|
||||
{
|
||||
href: '/allocations/allocations-list',
|
||||
label: 'Allocations',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiDatabaseArrowRight' in icon ? icon['mdiDatabaseArrowRight' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_ALLOCATIONS'
|
||||
},
|
||||
{
|
||||
href: '/budget_reallocations/budget_reallocations-list',
|
||||
label: 'Budget reallocations',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiSwapHorizontal' in icon ? icon['mdiSwapHorizontal' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_BUDGET_REALLOCATIONS'
|
||||
},
|
||||
{
|
||||
href: '/procurement_plans/procurement_plans-list',
|
||||
label: 'Procurement plans',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiClipboardList' in icon ? icon['mdiClipboardList' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PROCUREMENT_PLANS'
|
||||
},
|
||||
{
|
||||
href: '/requisitions/requisitions-list',
|
||||
label: 'Requisitions',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFileDocumentEdit' in icon ? icon['mdiFileDocumentEdit' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_REQUISITIONS'
|
||||
},
|
||||
{
|
||||
href: '/tenders/tenders-list',
|
||||
label: 'Tenders',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiGavel' in icon ? icon['mdiGavel' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_TENDERS'
|
||||
},
|
||||
{
|
||||
href: '/vendors/vendors-list',
|
||||
label: 'Vendors',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiTruckFast' in icon ? icon['mdiTruckFast' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_VENDORS'
|
||||
},
|
||||
{
|
||||
href: '/vendor_compliance_documents/vendor_compliance_documents-list',
|
||||
label: 'Vendor compliance documents',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFileCertificate' in icon ? icon['mdiFileCertificate' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_VENDOR_COMPLIANCE_DOCUMENTS'
|
||||
},
|
||||
{
|
||||
href: '/bids/bids-list',
|
||||
label: 'Bids',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFileSign' in icon ? icon['mdiFileSign' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_BIDS'
|
||||
},
|
||||
{
|
||||
href: '/bid_evaluations/bid_evaluations-list',
|
||||
label: 'Bid evaluations',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiClipboardCheck' in icon ? icon['mdiClipboardCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_BID_EVALUATIONS'
|
||||
},
|
||||
{
|
||||
href: '/awards/awards-list',
|
||||
label: 'Awards',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiTrophyAward' in icon ? icon['mdiTrophyAward' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_AWARDS'
|
||||
},
|
||||
{
|
||||
href: '/programs/programs-list',
|
||||
label: 'Programs',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiViewGridOutline' in icon ? icon['mdiViewGridOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PROGRAMS'
|
||||
},
|
||||
{
|
||||
href: '/projects/projects-list',
|
||||
label: 'Delivery Portfolio',
|
||||
icon: icon.mdiChartTimelineVariant,
|
||||
permissions: 'READ_PROJECTS',
|
||||
roles: [WORKSPACE_ROLES.projectDeliveryLead],
|
||||
withDevider: true,
|
||||
label: 'Projects',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiBriefcaseCheck' in icon ? icon['mdiBriefcaseCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PROJECTS'
|
||||
},
|
||||
{
|
||||
href: '/project_milestones/project_milestones-list',
|
||||
label: 'Milestone Tracker',
|
||||
icon: icon.mdiFlagCheckered,
|
||||
permissions: 'READ_PROJECT_MILESTONES',
|
||||
roles: [WORKSPACE_ROLES.projectDeliveryLead],
|
||||
label: 'Project milestones',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFlagCheckered' in icon ? icon['mdiFlagCheckered' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PROJECT_MILESTONES'
|
||||
},
|
||||
{
|
||||
href: '/risks/risks-list',
|
||||
label: 'Risks',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiAlertOctagon' in icon ? icon['mdiAlertOctagon' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_RISKS'
|
||||
},
|
||||
{
|
||||
href: '/issues/issues-list',
|
||||
label: 'Issues',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiBug' in icon ? icon['mdiBug' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_ISSUES'
|
||||
},
|
||||
{
|
||||
href: '/field_verifications/field_verifications-list',
|
||||
label: 'Field verifications',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiMapMarkerCheck' in icon ? icon['mdiMapMarkerCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_FIELD_VERIFICATIONS'
|
||||
},
|
||||
{
|
||||
href: '/contracts/contracts-list',
|
||||
label: 'Contracts',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFileDocumentOutline' in icon ? icon['mdiFileDocumentOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_CONTRACTS'
|
||||
},
|
||||
{
|
||||
href: '/contract_amendments/contract_amendments-list',
|
||||
label: 'Contract amendments',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFileReplaceOutline' in icon ? icon['mdiFileReplaceOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_CONTRACT_AMENDMENTS'
|
||||
},
|
||||
{
|
||||
href: '/contract_milestones/contract_milestones-list',
|
||||
label: 'Contract milestones',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiTimelineCheck' in icon ? icon['mdiTimelineCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_CONTRACT_MILESTONES'
|
||||
},
|
||||
{
|
||||
href: '/grants/grants-list',
|
||||
label: 'Grants',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiHandCoin' in icon ? icon['mdiHandCoin' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_GRANTS'
|
||||
},
|
||||
{
|
||||
href: '/beneficiaries/beneficiaries-list',
|
||||
label: 'Beneficiaries',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiAccountGroup' in icon ? icon['mdiAccountGroup' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_BENEFICIARIES'
|
||||
},
|
||||
{
|
||||
href: '/grant_applications/grant_applications-list',
|
||||
label: 'Grant applications',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFileDocumentMultiple' in icon ? icon['mdiFileDocumentMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_GRANT_APPLICATIONS'
|
||||
},
|
||||
{
|
||||
href: '/grant_evaluations/grant_evaluations-list',
|
||||
label: 'Grant evaluations',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiStarCheck' in icon ? icon['mdiStarCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_GRANT_EVALUATIONS'
|
||||
},
|
||||
{
|
||||
href: '/grant_tranches/grant_tranches-list',
|
||||
label: 'Grant tranches',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiCashCheck' in icon ? icon['mdiCashCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_GRANT_TRANCHES'
|
||||
},
|
||||
{
|
||||
href: '/expense_categories/expense_categories-list',
|
||||
label: 'Expense categories',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiTagMultiple' in icon ? icon['mdiTagMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_EXPENSE_CATEGORIES'
|
||||
},
|
||||
{
|
||||
href: '/invoices/invoices-list',
|
||||
label: 'Invoices',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiReceiptText' in icon ? icon['mdiReceiptText' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_INVOICES'
|
||||
},
|
||||
{
|
||||
href: '/payment_requests/payment_requests-list',
|
||||
label: 'Payment requests',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiCashFast' in icon ? icon['mdiCashFast' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PAYMENT_REQUESTS'
|
||||
},
|
||||
{
|
||||
href: '/payment_batches/payment_batches-list',
|
||||
label: 'Payment batches',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiPackageVariantClosed' in icon ? icon['mdiPackageVariantClosed' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PAYMENT_BATCHES'
|
||||
},
|
||||
{
|
||||
href: '/payments/payments-list',
|
||||
label: 'Payments',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiBankTransfer' in icon ? icon['mdiBankTransfer' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_PAYMENTS'
|
||||
},
|
||||
{
|
||||
href: '/obligations/obligations-list',
|
||||
label: 'Obligations',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiBookArrowDown' in icon ? icon['mdiBookArrowDown' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_OBLIGATIONS'
|
||||
},
|
||||
{
|
||||
href: '/ledger_entries/ledger_entries-list',
|
||||
label: 'Ledger entries',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiBookOpenPageVariant' in icon ? icon['mdiBookOpenPageVariant' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_LEDGER_ENTRIES'
|
||||
},
|
||||
{
|
||||
href: '/documents/documents-list',
|
||||
label: 'Documents',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFolderFile' in icon ? icon['mdiFolderFile' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_DOCUMENTS'
|
||||
},
|
||||
{
|
||||
href: '/compliance_alerts/compliance_alerts-list',
|
||||
label: 'Compliance alerts',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiShieldAlert' in icon ? icon['mdiShieldAlert' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_COMPLIANCE_ALERTS'
|
||||
},
|
||||
...sharedEntityNavigation,
|
||||
{
|
||||
href: '/profile',
|
||||
label: 'Profile',
|
||||
icon: icon.mdiAccountCircle,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
href: '/api-docs',
|
||||
target: '_blank',
|
||||
label: 'Swagger API',
|
||||
icon: icon.mdiFileCode,
|
||||
permissions: 'READ_API_DOCS',
|
||||
permissions: 'READ_API_DOCS'
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,29 @@ const EditAllocationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Allocation"
|
||||
pluralLabel="Allocations"
|
||||
listHref="/allocations/allocations-list"
|
||||
viewHref={`/allocations/allocations-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='BudgetLine' labelFor='budget_line'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='BudgetLine' labelFor='budget_line'>
|
||||
<Field
|
||||
name='budget_line'
|
||||
id='budget_line'
|
||||
@ -1101,7 +1115,6 @@ const EditAllocationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/allocations/allocations-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,841 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/allocations/allocationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/allocations/allocationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const AllocationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Allocation"
|
||||
pluralLabel="Allocations"
|
||||
stateKey="allocations"
|
||||
recordKey="allocations"
|
||||
fetchRecord={fetch}
|
||||
listHref="/allocations/allocations-list"
|
||||
editHref={(id) => `/allocations/allocations-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
AllocationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_ALLOCATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const AllocationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { allocations } = useAppSelector((state) => state.allocations)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View allocations')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View allocations')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/allocations/allocations-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>BudgetLine</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{allocations?.budget_line?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Province</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{allocations?.province?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Department</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{allocations?.department?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AllocationAmount</p>
|
||||
<p>{allocations?.amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{allocations?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='AllocatedAt'>
|
||||
{allocations.allocated_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={allocations.allocated_at ?
|
||||
new Date(
|
||||
dayjs(allocations.allocated_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No AllocatedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{allocations?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={allocations?.notes} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{allocations?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/allocations/allocations-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AllocationsView;
|
||||
AllocationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_ALLOCATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default AllocationsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,29 @@ const EditApproval_stepsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Approval Step"
|
||||
pluralLabel="Approval Steps"
|
||||
listHref="/approval_steps/approval_steps-list"
|
||||
viewHref={`/approval_steps/approval_steps-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Workflow' labelFor='workflow'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Workflow' labelFor='workflow'>
|
||||
<Field
|
||||
name='workflow'
|
||||
id='workflow'
|
||||
@ -1093,7 +1107,6 @@ const EditApproval_stepsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/approval_steps/approval_steps-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,954 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/approval_steps/approval_stepsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/approval_steps/approval_stepsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const ApprovalStepsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Approval Step"
|
||||
pluralLabel="Approval Steps"
|
||||
stateKey="approval_steps"
|
||||
recordKey="approval_steps"
|
||||
fetchRecord={fetch}
|
||||
listHref="/approval_steps/approval_steps-list"
|
||||
editHref={(id) => `/approval_steps/approval_steps-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
ApprovalStepsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_APPROVAL_STEPS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Approval_stepsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { approval_steps } = useAppSelector((state) => state.approval_steps)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View approval_steps')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View approval_steps')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/approval_steps/approval_steps-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Workflow</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{approval_steps?.workflow?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>StepOrder</p>
|
||||
<p>{approval_steps?.step_order || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>StepName</p>
|
||||
<p>{approval_steps?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ApproverRole</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{approval_steps?.approver_role?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ApproverDepartment</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{approval_steps?.approver_department?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>MinimumAmount</p>
|
||||
<p>{approval_steps?.min_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>MaximumAmount</p>
|
||||
<p>{approval_steps?.max_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='RequiresComment'>
|
||||
<SwitchField
|
||||
field={{name: 'requires_comment', value: approval_steps?.requires_comment}}
|
||||
form={{setFieldValue: () => null}}
|
||||
disabled
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{approval_steps?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Approvals CurrentStep</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>RecordType</th>
|
||||
|
||||
|
||||
|
||||
<th>RecordKey</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>RequestedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>DecidedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>DecisionComment</th>
|
||||
|
||||
|
||||
|
||||
<th>RejectionReason</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{approval_steps.approvals_step && Array.isArray(approval_steps.approvals_step) &&
|
||||
approval_steps.approvals_step.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/approvals/approvals-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="record_type">
|
||||
{ item.record_type }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="record_key">
|
||||
{ item.record_key }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="requested_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.requested_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="decided_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.decided_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="decision_comment">
|
||||
{ item.decision_comment }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="rejection_reason">
|
||||
{ item.rejection_reason }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!approval_steps?.approvals_step?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/approval_steps/approval_steps-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApprovalStepsView;
|
||||
Approval_stepsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_APPROVAL_STEPS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Approval_stepsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -232,14 +231,28 @@ const EditApproval_workflowsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Approval Workflow"
|
||||
pluralLabel="Approval Workflows"
|
||||
listHref="/approval_workflows/approval_workflows-list"
|
||||
viewHref={`/approval_workflows/approval_workflows-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -550,7 +563,6 @@ const EditApproval_workflowsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/approval_workflows/approval_workflows-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,606 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/approval_workflows/approval_workflowsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/approval_workflows/approval_workflowsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const ApprovalWorkflowsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Approval Workflow"
|
||||
pluralLabel="Approval Workflows"
|
||||
stateKey="approval_workflows"
|
||||
recordKey="approval_workflows"
|
||||
fetchRecord={fetch}
|
||||
listHref="/approval_workflows/approval_workflows-list"
|
||||
editHref={(id) => `/approval_workflows/approval_workflows-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
ApprovalWorkflowsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_APPROVAL_WORKFLOWS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Approval_workflowsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { approval_workflows } = useAppSelector((state) => state.approval_workflows)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View approval_workflows')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View approval_workflows')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/approval_workflows/approval_workflows-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{approval_workflows?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>WorkflowName</p>
|
||||
<p>{approval_workflows?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Module</p>
|
||||
<p>{approval_workflows?.module ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordType</p>
|
||||
<p>{approval_workflows?.record_type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{approval_workflows?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Approval_steps Workflow</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<th>StepOrder</th>
|
||||
|
||||
|
||||
|
||||
<th>StepName</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>MinimumAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>MaximumAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>RequiresComment</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{approval_workflows.approval_steps_workflow && Array.isArray(approval_workflows.approval_steps_workflow) &&
|
||||
approval_workflows.approval_steps_workflow.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/approval_steps/approval_steps-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="step_order">
|
||||
{ item.step_order }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="min_amount">
|
||||
{ item.min_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="max_amount">
|
||||
{ item.max_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="requires_comment">
|
||||
{ dataFormatter.booleanFormatter(item.requires_comment) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!approval_workflows?.approval_steps_workflow?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Approvals Workflow</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>RecordType</th>
|
||||
|
||||
|
||||
|
||||
<th>RecordKey</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>RequestedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>DecidedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>DecisionComment</th>
|
||||
|
||||
|
||||
|
||||
<th>RejectionReason</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{approval_workflows.approvals_workflow && Array.isArray(approval_workflows.approvals_workflow) &&
|
||||
approval_workflows.approvals_workflow.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/approvals/approvals-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="record_type">
|
||||
{ item.record_type }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="record_key">
|
||||
{ item.record_key }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="requested_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.requested_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="decided_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.decided_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="decision_comment">
|
||||
{ item.decision_comment }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="rejection_reason">
|
||||
{ item.rejection_reason }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!approval_workflows?.approvals_workflow?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/approval_workflows/approval_workflows-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApprovalWorkflowsView;
|
||||
Approval_workflowsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_APPROVAL_WORKFLOWS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Approval_workflowsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -428,14 +427,28 @@ const EditApprovalsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Approval"
|
||||
pluralLabel="Approvals"
|
||||
listHref="/approvals/approvals-list"
|
||||
viewHref={`/approvals/approvals-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1399,7 +1412,6 @@ const EditApprovalsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/approvals/approvals-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,28 @@ const EditAudit_logsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Audit Log"
|
||||
pluralLabel="Audit Logs"
|
||||
listHref="/audit_logs/audit_logs-list"
|
||||
viewHref={`/audit_logs/audit_logs-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -878,7 +891,6 @@ const EditAudit_logsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/audit_logs/audit_logs-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,636 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/audit_logs/audit_logsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/audit_logs/audit_logsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const AuditLogsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Audit Log"
|
||||
pluralLabel="Audit Logs"
|
||||
stateKey="audit_logs"
|
||||
recordKey="audit_logs"
|
||||
fetchRecord={fetch}
|
||||
listHref="/audit_logs/audit_logs-list"
|
||||
editHref={(id) => `/audit_logs/audit_logs-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
AuditLogsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_AUDIT_LOGS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Audit_logsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { audit_logs } = useAppSelector((state) => state.audit_logs)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View audit_logs')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View audit_logs')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/audit_logs/audit_logs-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{audit_logs?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ActorUser</p>
|
||||
|
||||
|
||||
<p>{audit_logs?.actor_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Action</p>
|
||||
<p>{audit_logs?.action}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>EntityName</p>
|
||||
<p>{audit_logs?.entity_name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordKey</p>
|
||||
<p>{audit_logs?.record_key}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={audit_logs?.summary} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='OccurredAt'>
|
||||
{audit_logs.occurred_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={audit_logs.occurred_at ?
|
||||
new Date(
|
||||
dayjs(audit_logs.occurred_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No OccurredAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>IPAddress</p>
|
||||
<p>{audit_logs?.ip_address}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={audit_logs?.user_agent} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/audit_logs/audit_logs-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuditLogsView;
|
||||
Audit_logsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_AUDIT_LOGS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Audit_logsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,28 @@ const EditAwardsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Award"
|
||||
pluralLabel="Awards"
|
||||
listHref="/awards/awards-list"
|
||||
viewHref={`/awards/awards-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1003,7 +1016,6 @@ const EditAwardsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/awards/awards-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,900 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/awards/awardsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/awards/awardsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const AwardsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Award"
|
||||
pluralLabel="Awards"
|
||||
stateKey="awards"
|
||||
recordKey="awards"
|
||||
fetchRecord={fetch}
|
||||
listHref="/awards/awards-list"
|
||||
editHref={(id) => `/awards/awards-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
AwardsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_AWARDS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const AwardsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { awards } = useAppSelector((state) => state.awards)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View awards')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View awards')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/awards/awards-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{awards?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Tender</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{awards?.tender?.tender_number ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>WinningBid</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{awards?.winning_bid?.bid_reference ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AwardNumber</p>
|
||||
<p>{awards?.award_number}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AwardAmount</p>
|
||||
<p>{awards?.award_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{awards?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='DecisionDate'>
|
||||
{awards.decision_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={awards.decision_date ?
|
||||
new Date(
|
||||
dayjs(awards.decision_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No DecisionDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{awards?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AwardMemo</p>
|
||||
{awards.award_memo
|
||||
? <p dangerouslySetInnerHTML={{__html: awards.award_memo}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Contracts Award</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ContractNumber</th>
|
||||
|
||||
|
||||
|
||||
<th>ContractTitle</th>
|
||||
|
||||
|
||||
|
||||
<th>ContractType</th>
|
||||
|
||||
|
||||
|
||||
<th>ContractValue</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>StartDate</th>
|
||||
|
||||
|
||||
|
||||
<th>EndDate</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>RetentionPercent</th>
|
||||
|
||||
|
||||
|
||||
<th>PenaltyRatePercent</th>
|
||||
|
||||
|
||||
|
||||
<th>SignedAt</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{awards.contracts_award && Array.isArray(awards.contracts_award) &&
|
||||
awards.contracts_award.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/contracts/contracts-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="contract_number">
|
||||
{ item.contract_number }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="title">
|
||||
{ item.title }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="contract_type">
|
||||
{ item.contract_type }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="contract_value">
|
||||
{ item.contract_value }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="start_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.start_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="end_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.end_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="retention_percent">
|
||||
{ item.retention_percent }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="penalty_rate_percent">
|
||||
{ item.penalty_rate_percent }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="signed_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.signed_at) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!awards?.contracts_award?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/awards/awards-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AwardsView;
|
||||
AwardsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_AWARDS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default AwardsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -428,14 +427,29 @@ const EditBeneficiariesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Beneficiary"
|
||||
pluralLabel="Beneficiaries"
|
||||
listHref="/beneficiaries/beneficiaries-list"
|
||||
viewHref={`/beneficiaries/beneficiaries-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Grant' labelFor='grant'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Grant' labelFor='grant'>
|
||||
<Field
|
||||
name='grant'
|
||||
id='grant'
|
||||
@ -1211,7 +1225,6 @@ const EditBeneficiariesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/beneficiaries/beneficiaries-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,925 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/beneficiaries/beneficiariesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/beneficiaries/beneficiariesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const BeneficiariesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Beneficiary"
|
||||
pluralLabel="Beneficiaries"
|
||||
stateKey="beneficiaries"
|
||||
recordKey="beneficiaries"
|
||||
fetchRecord={fetch}
|
||||
listHref="/beneficiaries/beneficiaries-list"
|
||||
editHref={(id) => `/beneficiaries/beneficiaries-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
BeneficiariesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_BENEFICIARIES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const BeneficiariesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { beneficiaries } = useAppSelector((state) => state.beneficiaries)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View beneficiaries')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View beneficiaries')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/beneficiaries/beneficiaries-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Grant</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{beneficiaries?.grant?.call_reference ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Province</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{beneficiaries?.province?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>BeneficiaryName</p>
|
||||
<p>{beneficiaries?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>BeneficiaryType</p>
|
||||
<p>{beneficiaries?.beneficiary_type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RegistrationNumber</p>
|
||||
<p>{beneficiaries?.registration_number}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ContactEmail</p>
|
||||
<p>{beneficiaries?.contact_email}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ContactPhone</p>
|
||||
<p>{beneficiaries?.contact_phone}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ApprovedAmount</p>
|
||||
<p>{beneficiaries?.approved_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{beneficiaries?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{beneficiaries?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='ApprovedAt'>
|
||||
{beneficiaries.approved_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={beneficiaries.approved_at ?
|
||||
new Date(
|
||||
dayjs(beneficiaries.approved_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No ApprovedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{beneficiaries?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Grant_applications Beneficiary</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ApplicationReference</th>
|
||||
|
||||
|
||||
|
||||
<th>SubmittedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>RequestedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{beneficiaries.grant_applications_beneficiary && Array.isArray(beneficiaries.grant_applications_beneficiary) &&
|
||||
beneficiaries.grant_applications_beneficiary.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/grant_applications/grant_applications-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="application_reference">
|
||||
{ item.application_reference }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="submitted_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.submitted_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="requested_amount">
|
||||
{ item.requested_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!beneficiaries?.grant_applications_beneficiary?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/beneficiaries/beneficiaries-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BeneficiariesView;
|
||||
BeneficiariesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_BENEFICIARIES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default BeneficiariesView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,29 @@ const EditBid_evaluationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Bid Evaluation"
|
||||
pluralLabel="Bid Evaluations"
|
||||
listHref="/bid_evaluations/bid_evaluations-list"
|
||||
viewHref={`/bid_evaluations/bid_evaluations-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Bid' labelFor='bid'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Bid' labelFor='bid'>
|
||||
<Field
|
||||
name='bid'
|
||||
id='bid'
|
||||
@ -993,7 +1007,6 @@ const EditBid_evaluationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/bid_evaluations/bid_evaluations-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,739 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/bid_evaluations/bid_evaluationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/bid_evaluations/bid_evaluationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const BidEvaluationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Bid Evaluation"
|
||||
pluralLabel="Bid Evaluations"
|
||||
stateKey="bid_evaluations"
|
||||
recordKey="bid_evaluations"
|
||||
fetchRecord={fetch}
|
||||
listHref="/bid_evaluations/bid_evaluations-list"
|
||||
editHref={(id) => `/bid_evaluations/bid_evaluations-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
BidEvaluationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_BID_EVALUATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Bid_evaluationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { bid_evaluations } = useAppSelector((state) => state.bid_evaluations)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View bid_evaluations')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View bid_evaluations')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/bid_evaluations/bid_evaluations-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Bid</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{bid_evaluations?.bid?.bid_reference ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>LeadEvaluator</p>
|
||||
|
||||
|
||||
<p>{bid_evaluations?.lead_evaluator_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>TechnicalScore</p>
|
||||
<p>{bid_evaluations?.technical_score || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FinancialScore</p>
|
||||
<p>{bid_evaluations?.financial_score || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>TotalScore</p>
|
||||
<p>{bid_evaluations?.total_score || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Recommendation</p>
|
||||
<p>{bid_evaluations?.recommendation ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={bid_evaluations?.justification} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='EvaluatedAt'>
|
||||
{bid_evaluations.evaluated_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={bid_evaluations.evaluated_at ?
|
||||
new Date(
|
||||
dayjs(bid_evaluations.evaluated_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No EvaluatedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{bid_evaluations?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/bid_evaluations/bid_evaluations-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BidEvaluationsView;
|
||||
Bid_evaluationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_BID_EVALUATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Bid_evaluationsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,29 @@ const EditBidsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Bid"
|
||||
pluralLabel="Bids"
|
||||
listHref="/bids/bids-list"
|
||||
viewHref={`/bids/bids-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Tender' labelFor='tender'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Tender' labelFor='tender'>
|
||||
<Field
|
||||
name='tender'
|
||||
id='tender'
|
||||
@ -1000,7 +1014,6 @@ const EditBidsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/bids/bids-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,927 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/bids/bidsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/bids/bidsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const BidsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Bid"
|
||||
pluralLabel="Bids"
|
||||
stateKey="bids"
|
||||
recordKey="bids"
|
||||
fetchRecord={fetch}
|
||||
listHref="/bids/bids-list"
|
||||
editHref={(id) => `/bids/bids-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
BidsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_BIDS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const BidsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { bids } = useAppSelector((state) => state.bids)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View bids')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View bids')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/bids/bids-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Tender</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{bids?.tender?.tender_number ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Vendor</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{bids?.vendor?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>BidReference</p>
|
||||
<p>{bids?.bid_reference}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='SubmittedAt'>
|
||||
{bids.submitted_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={bids.submitted_at ?
|
||||
new Date(
|
||||
dayjs(bids.submitted_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No SubmittedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>BidAmount</p>
|
||||
<p>{bids?.bid_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{bids?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{bids?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={bids?.notes} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{bids?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Bid_evaluations Bid</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>TechnicalScore</th>
|
||||
|
||||
|
||||
|
||||
<th>FinancialScore</th>
|
||||
|
||||
|
||||
|
||||
<th>TotalScore</th>
|
||||
|
||||
|
||||
|
||||
<th>Recommendation</th>
|
||||
|
||||
|
||||
|
||||
<th>Justification</th>
|
||||
|
||||
|
||||
|
||||
<th>EvaluatedAt</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{bids.bid_evaluations_bid && Array.isArray(bids.bid_evaluations_bid) &&
|
||||
bids.bid_evaluations_bid.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/bid_evaluations/bid_evaluations-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="technical_score">
|
||||
{ item.technical_score }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="financial_score">
|
||||
{ item.financial_score }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="total_score">
|
||||
{ item.total_score }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="recommendation">
|
||||
{ item.recommendation }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="justification">
|
||||
{ item.justification }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="evaluated_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.evaluated_at) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!bids?.bid_evaluations_bid?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Awards WinningBid</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>AwardNumber</th>
|
||||
|
||||
|
||||
|
||||
<th>AwardAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>DecisionDate</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{bids.awards_winning_bid && Array.isArray(bids.awards_winning_bid) &&
|
||||
bids.awards_winning_bid.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/awards/awards-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="award_number">
|
||||
{ item.award_number }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="award_amount">
|
||||
{ item.award_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="decision_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.decision_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!bids?.awards_winning_bid?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/bids/bids-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BidsView;
|
||||
BidsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_BIDS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default BidsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -400,14 +399,29 @@ const EditBudget_linesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Budget Line"
|
||||
pluralLabel="Budget Lines"
|
||||
listHref="/budget_lines/budget_lines-list"
|
||||
viewHref={`/budget_lines/budget_lines-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='BudgetProgram' labelFor='budget_program'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='BudgetProgram' labelFor='budget_program'>
|
||||
<Field
|
||||
name='budget_program'
|
||||
id='budget_program'
|
||||
@ -1024,7 +1038,6 @@ const EditBudget_linesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/budget_lines/budget_lines-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,28 @@ const EditBudget_programsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Budget Program"
|
||||
pluralLabel="Budget Programs"
|
||||
listHref="/budget_programs/budget_programs-list"
|
||||
viewHref={`/budget_programs/budget_programs-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -991,7 +1004,6 @@ const EditBudget_programsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/budget_programs/budget_programs-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,852 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/budget_programs/budget_programsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/budget_programs/budget_programsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const BudgetProgramsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Budget Program"
|
||||
pluralLabel="Budget Programs"
|
||||
stateKey="budget_programs"
|
||||
recordKey="budget_programs"
|
||||
fetchRecord={fetch}
|
||||
listHref="/budget_programs/budget_programs-list"
|
||||
editHref={(id) => `/budget_programs/budget_programs-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
BudgetProgramsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_BUDGET_PROGRAMS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Budget_programsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { budget_programs } = useAppSelector((state) => state.budget_programs)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View budget_programs')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View budget_programs')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/budget_programs/budget_programs-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{budget_programs?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FiscalYear</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{budget_programs?.fiscal_year?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FundingSource</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{budget_programs?.funding_source?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ProgramName</p>
|
||||
<p>{budget_programs?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ProgramCode</p>
|
||||
<p>{budget_programs?.code}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={budget_programs?.objective} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{budget_programs?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ApprovedAmount</p>
|
||||
<p>{budget_programs?.approved_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{budget_programs?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Budget_lines BudgetProgram</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<th>BudgetLineName</th>
|
||||
|
||||
|
||||
|
||||
<th>BudgetLineCode</th>
|
||||
|
||||
|
||||
|
||||
<th>Description</th>
|
||||
|
||||
|
||||
|
||||
<th>Category</th>
|
||||
|
||||
|
||||
|
||||
<th>ApprovedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>CommittedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>DisbursedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{budget_programs.budget_lines_budget_program && Array.isArray(budget_programs.budget_lines_budget_program) &&
|
||||
budget_programs.budget_lines_budget_program.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/budget_lines/budget_lines-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="code">
|
||||
{ item.code }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="description">
|
||||
{ item.description }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="category">
|
||||
{ item.category }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="approved_amount">
|
||||
{ item.approved_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="committed_amount">
|
||||
{ item.committed_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="disbursed_amount">
|
||||
{ item.disbursed_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!budget_programs?.budget_lines_budget_program?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/budget_programs/budget_programs-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BudgetProgramsView;
|
||||
Budget_programsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_BUDGET_PROGRAMS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Budget_programsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -372,14 +371,28 @@ const EditBudget_reallocationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Budget Reallocation"
|
||||
pluralLabel="Budget Reallocations"
|
||||
listHref="/budget_reallocations/budget_reallocations-list"
|
||||
viewHref={`/budget_reallocations/budget_reallocations-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1272,7 +1285,6 @@ const EditBudget_reallocationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/budget_reallocations/budget_reallocations-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,975 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/budget_reallocations/budget_reallocationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/budget_reallocations/budget_reallocationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const BudgetReallocationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Budget Reallocation"
|
||||
pluralLabel="Budget Reallocations"
|
||||
stateKey="budget_reallocations"
|
||||
recordKey="budget_reallocations"
|
||||
fetchRecord={fetch}
|
||||
listHref="/budget_reallocations/budget_reallocations-list"
|
||||
editHref={(id) => `/budget_reallocations/budget_reallocations-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
BudgetReallocationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_BUDGET_REALLOCATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Budget_reallocationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { budget_reallocations } = useAppSelector((state) => state.budget_reallocations)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View budget_reallocations')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View budget_reallocations')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/budget_reallocations/budget_reallocations-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{budget_reallocations?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FiscalYear</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{budget_reallocations?.fiscal_year?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FromBudgetLine</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{budget_reallocations?.from_budget_line?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ToBudgetLine</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{budget_reallocations?.to_budget_line?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Amount</p>
|
||||
<p>{budget_reallocations?.amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{budget_reallocations?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{budget_reallocations?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RequestedBy</p>
|
||||
|
||||
|
||||
<p>{budget_reallocations?.requested_by_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='RequestedAt'>
|
||||
{budget_reallocations.requested_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={budget_reallocations.requested_at ?
|
||||
new Date(
|
||||
dayjs(budget_reallocations.requested_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No RequestedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={budget_reallocations?.justification} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/budget_reallocations/budget_reallocations-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BudgetReallocationsView;
|
||||
Budget_reallocationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_BUDGET_REALLOCATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Budget_reallocationsView;
|
||||
@ -1,7 +0,0 @@
|
||||
import ExecutiveSummaryPage from './executive-summary';
|
||||
|
||||
const ComplianceDeskPage: any = ExecutiveSummaryPage;
|
||||
|
||||
ComplianceDeskPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
|
||||
|
||||
export default ComplianceDeskPage;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -400,14 +399,28 @@ const EditCompliance_alertsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Compliance Alert"
|
||||
pluralLabel="Compliance Alerts"
|
||||
listHref="/compliance_alerts/compliance_alerts-list"
|
||||
viewHref={`/compliance_alerts/compliance_alerts-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1044,7 +1057,6 @@ const EditCompliance_alertsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/compliance_alerts/compliance_alerts-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,713 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/compliance_alerts/compliance_alertsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/compliance_alerts/compliance_alertsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const ComplianceAlertsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Compliance Alert"
|
||||
pluralLabel="Compliance Alerts"
|
||||
stateKey="compliance_alerts"
|
||||
recordKey="compliance_alerts"
|
||||
fetchRecord={fetch}
|
||||
listHref="/compliance_alerts/compliance_alerts-list"
|
||||
editHref={(id) => `/compliance_alerts/compliance_alerts-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
ComplianceAlertsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_COMPLIANCE_ALERTS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Compliance_alertsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { compliance_alerts } = useAppSelector((state) => state.compliance_alerts)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View compliance_alerts')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View compliance_alerts')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/compliance_alerts/compliance_alerts-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{compliance_alerts?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AlertType</p>
|
||||
<p>{compliance_alerts?.alert_type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Severity</p>
|
||||
<p>{compliance_alerts?.severity ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Title</p>
|
||||
<p>{compliance_alerts?.title}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Details</p>
|
||||
{compliance_alerts.details
|
||||
? <p dangerouslySetInnerHTML={{__html: compliance_alerts.details}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordType</p>
|
||||
<p>{compliance_alerts?.record_type}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordKey</p>
|
||||
<p>{compliance_alerts?.record_key}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{compliance_alerts?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AssignedTo</p>
|
||||
|
||||
|
||||
<p>{compliance_alerts?.assigned_to_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='DetectedAt'>
|
||||
{compliance_alerts.detected_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={compliance_alerts.detected_at ?
|
||||
new Date(
|
||||
dayjs(compliance_alerts.detected_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No DetectedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='DueAt'>
|
||||
{compliance_alerts.due_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={compliance_alerts.due_at ?
|
||||
new Date(
|
||||
dayjs(compliance_alerts.due_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No DueAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/compliance_alerts/compliance_alerts-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ComplianceAlertsView;
|
||||
Compliance_alertsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_COMPLIANCE_ALERTS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Compliance_alertsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,29 @@ const EditContract_amendmentsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Contract Amendment"
|
||||
pluralLabel="Contract Amendments"
|
||||
listHref="/contract_amendments/contract_amendments-list"
|
||||
viewHref={`/contract_amendments/contract_amendments-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Contract' labelFor='contract'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Contract' labelFor='contract'>
|
||||
<Field
|
||||
name='contract'
|
||||
id='contract'
|
||||
@ -902,7 +916,6 @@ const EditContract_amendmentsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/contract_amendments/contract_amendments-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,641 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/contract_amendments/contract_amendmentsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/contract_amendments/contract_amendmentsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const ContractAmendmentsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Contract Amendment"
|
||||
pluralLabel="Contract Amendments"
|
||||
stateKey="contract_amendments"
|
||||
recordKey="contract_amendments"
|
||||
fetchRecord={fetch}
|
||||
listHref="/contract_amendments/contract_amendments-list"
|
||||
editHref={(id) => `/contract_amendments/contract_amendments-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
ContractAmendmentsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_CONTRACT_AMENDMENTS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Contract_amendmentsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { contract_amendments } = useAppSelector((state) => state.contract_amendments)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View contract_amendments')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View contract_amendments')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/contract_amendments/contract_amendments-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Contract</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{contract_amendments?.contract?.contract_number ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AmendmentNumber</p>
|
||||
<p>{contract_amendments?.amendment_number}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AmendmentType</p>
|
||||
<p>{contract_amendments?.amendment_type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ValueChangeAmount</p>
|
||||
<p>{contract_amendments?.value_change_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>DaysExtension</p>
|
||||
<p>{contract_amendments?.days_extension || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Description</p>
|
||||
{contract_amendments.description
|
||||
? <p dangerouslySetInnerHTML={{__html: contract_amendments.description}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{contract_amendments?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='EffectiveDate'>
|
||||
{contract_amendments.effective_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={contract_amendments.effective_date ?
|
||||
new Date(
|
||||
dayjs(contract_amendments.effective_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No EffectiveDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{contract_amendments?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/contract_amendments/contract_amendments-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContractAmendmentsView;
|
||||
Contract_amendmentsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_CONTRACT_AMENDMENTS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Contract_amendmentsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -316,14 +315,29 @@ const EditContract_milestonesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Contract Milestone"
|
||||
pluralLabel="Contract Milestones"
|
||||
listHref="/contract_milestones/contract_milestones-list"
|
||||
viewHref={`/contract_milestones/contract_milestones-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Contract' labelFor='contract'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Contract' labelFor='contract'>
|
||||
<Field
|
||||
name='contract'
|
||||
id='contract'
|
||||
@ -837,7 +851,6 @@ const EditContract_milestonesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/contract_milestones/contract_milestones-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,617 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/contract_milestones/contract_milestonesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/contract_milestones/contract_milestonesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const ContractMilestonesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Contract Milestone"
|
||||
pluralLabel="Contract Milestones"
|
||||
stateKey="contract_milestones"
|
||||
recordKey="contract_milestones"
|
||||
fetchRecord={fetch}
|
||||
listHref="/contract_milestones/contract_milestones-list"
|
||||
editHref={(id) => `/contract_milestones/contract_milestones-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
ContractMilestonesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_CONTRACT_MILESTONES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Contract_milestonesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { contract_milestones } = useAppSelector((state) => state.contract_milestones)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View contract_milestones')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View contract_milestones')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/contract_milestones/contract_milestones-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Contract</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{contract_milestones?.contract?.contract_number ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>MilestoneName</p>
|
||||
<p>{contract_milestones?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>DeliverableDescription</p>
|
||||
{contract_milestones.deliverable_description
|
||||
? <p dangerouslySetInnerHTML={{__html: contract_milestones.deliverable_description}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='DueDate'>
|
||||
{contract_milestones.due_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={contract_milestones.due_date ?
|
||||
new Date(
|
||||
dayjs(contract_milestones.due_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No DueDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='AcceptedAt'>
|
||||
{contract_milestones.accepted_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={contract_milestones.accepted_at ?
|
||||
new Date(
|
||||
dayjs(contract_milestones.accepted_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No AcceptedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{contract_milestones?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>PaymentPercent</p>
|
||||
<p>{contract_milestones?.payment_percent || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{contract_milestones?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/contract_milestones/contract_milestones-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContractMilestonesView;
|
||||
Contract_milestonesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_CONTRACT_MILESTONES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Contract_milestonesView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -540,14 +539,28 @@ const EditContractsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Contract"
|
||||
pluralLabel="Contracts"
|
||||
listHref="/contracts/contracts-list"
|
||||
viewHref={`/contracts/contracts-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1694,7 +1707,6 @@ const EditContractsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/contracts/contracts-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,7 +0,0 @@
|
||||
import ExecutiveSummaryPage from './executive-summary';
|
||||
|
||||
const DeliveryCommandPage: any = ExecutiveSummaryPage;
|
||||
|
||||
DeliveryCommandPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
|
||||
|
||||
export default DeliveryCommandPage;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -232,14 +231,28 @@ const EditDepartmentsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Department"
|
||||
pluralLabel="Departments"
|
||||
listHref="/departments/departments-list"
|
||||
viewHref={`/departments/departments-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -623,7 +636,6 @@ const EditDepartmentsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/departments/departments-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,924 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/departments/departmentsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/departments/departmentsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const DepartmentsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Department"
|
||||
pluralLabel="Departments"
|
||||
stateKey="departments"
|
||||
recordKey="departments"
|
||||
fetchRecord={fetch}
|
||||
listHref="/departments/departments-list"
|
||||
editHref={(id) => `/departments/departments-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
DepartmentsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_DEPARTMENTS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const DepartmentsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { departments } = useAppSelector((state) => state.departments)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View departments')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View departments')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/departments/departments-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{departments?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>DepartmentName</p>
|
||||
<p>{departments?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>DepartmentCode</p>
|
||||
<p>{departments?.code}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ParentDepartment</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{departments?.parent_department?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{departments?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Approval_steps ApproverDepartment</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<th>StepOrder</th>
|
||||
|
||||
|
||||
|
||||
<th>StepName</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>MinimumAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>MaximumAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>RequiresComment</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{departments.approval_steps_approver_department && Array.isArray(departments.approval_steps_approver_department) &&
|
||||
departments.approval_steps_approver_department.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/approval_steps/approval_steps-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="step_order">
|
||||
{ item.step_order }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="min_amount">
|
||||
{ item.min_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="max_amount">
|
||||
{ item.max_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="requires_comment">
|
||||
{ dataFormatter.booleanFormatter(item.requires_comment) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!departments?.approval_steps_approver_department?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Allocations Department</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>AllocationAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>AllocatedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>Notes</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{departments.allocations_department && Array.isArray(departments.allocations_department) &&
|
||||
departments.allocations_department.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/allocations/allocations-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="amount">
|
||||
{ item.amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="allocated_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.allocated_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="notes">
|
||||
{ item.notes }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!departments?.allocations_department?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Procurement_plans Department</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>PlanName</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>EstimatedTotalAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>SubmittedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>ApprovedAt</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{departments.procurement_plans_department && Array.isArray(departments.procurement_plans_department) &&
|
||||
departments.procurement_plans_department.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/procurement_plans/procurement_plans-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="estimated_total_amount">
|
||||
{ item.estimated_total_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="submitted_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.submitted_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="approved_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.approved_at) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!departments?.procurement_plans_department?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Requisitions RequestingDepartment</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>RequisitionNumber</th>
|
||||
|
||||
|
||||
|
||||
<th>Title</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ProcurementMethod</th>
|
||||
|
||||
|
||||
|
||||
<th>EstimatedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>NeededByDate</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>SubmittedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>RejectionReason</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{departments.requisitions_requesting_department && Array.isArray(departments.requisitions_requesting_department) &&
|
||||
departments.requisitions_requesting_department.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/requisitions/requisitions-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="requisition_number">
|
||||
{ item.requisition_number }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="title">
|
||||
{ item.title }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="procurement_method">
|
||||
{ item.procurement_method }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="estimated_amount">
|
||||
{ item.estimated_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="needed_by_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.needed_by_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="submitted_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.submitted_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="rejection_reason">
|
||||
{ item.rejection_reason }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!departments?.requisitions_requesting_department?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/departments/departments-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DepartmentsView;
|
||||
DepartmentsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_DEPARTMENTS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default DepartmentsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -400,14 +399,28 @@ const EditDocumentsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Document"
|
||||
pluralLabel="Documents"
|
||||
listHref="/documents/documents-list"
|
||||
viewHref={`/documents/documents-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1041,7 +1054,6 @@ const EditDocumentsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/documents/documents-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,710 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/documents/documentsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/documents/documentsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const DocumentsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Document"
|
||||
pluralLabel="Documents"
|
||||
stateKey="documents"
|
||||
recordKey="documents"
|
||||
fetchRecord={fetch}
|
||||
listHref="/documents/documents-list"
|
||||
editHref={(id) => `/documents/documents-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
DocumentsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_DOCUMENTS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const DocumentsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { documents } = useAppSelector((state) => state.documents)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View documents')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View documents')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/documents/documents-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{documents?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Category</p>
|
||||
<p>{documents?.category ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Title</p>
|
||||
<p>{documents?.title}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={documents?.description} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordType</p>
|
||||
<p>{documents?.record_type}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordKey</p>
|
||||
<p>{documents?.record_key}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>File</p>
|
||||
{documents?.file?.length
|
||||
? dataFormatter.filesFormatter(documents.file).map(link => (
|
||||
<button
|
||||
key={link.publicUrl}
|
||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
||||
>
|
||||
{link.name}
|
||||
</button>
|
||||
)) : <p>No File</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>VersionNumber</p>
|
||||
<p>{documents?.version_number || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AccessLevel</p>
|
||||
<p>{documents?.access_level ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>UploadedBy</p>
|
||||
|
||||
|
||||
<p>{documents?.uploaded_by_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='UploadedAt'>
|
||||
{documents.uploaded_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={documents.uploaded_at ?
|
||||
new Date(
|
||||
dayjs(documents.uploaded_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No UploadedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/documents/documents-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DocumentsView;
|
||||
DocumentsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_DOCUMENTS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default DocumentsView;
|
||||
@ -11,18 +11,18 @@ export default function Error() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('Access denied')}</title>
|
||||
<title>{getPageTitle('Error')}</title>
|
||||
</Head>
|
||||
|
||||
<SectionFullScreen bg="pinkRed">
|
||||
<CardBox
|
||||
className="w-11/12 md:w-7/12 lg:w-6/12 xl:w-4/12 shadow-2xl"
|
||||
footer={<BaseButton href="/" label="Return to app" color="danger" />}
|
||||
footer={<BaseButton href="/dashboard" label="Done" color="danger" />}
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<h1 className="text-2xl">Access denied</h1>
|
||||
<h1 className="text-2xl">Unhandled exception</h1>
|
||||
|
||||
<p>You do not have permission to open that page with your current role.</p>
|
||||
<p>An Error Occurred</p>
|
||||
</div>
|
||||
</CardBox>
|
||||
</SectionFullScreen>
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
import ExecutiveSummaryPage from './executive-summary';
|
||||
|
||||
const ExecutiveCommandPage: any = ExecutiveSummaryPage;
|
||||
|
||||
ExecutiveCommandPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
|
||||
|
||||
export default ExecutiveCommandPage;
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -232,14 +231,28 @@ const EditExpense_categoriesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Expense Category"
|
||||
pluralLabel="Expense Categories"
|
||||
listHref="/expense_categories/expense_categories-list"
|
||||
viewHref={`/expense_categories/expense_categories-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -513,7 +526,6 @@ const EditExpense_categoriesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/expense_categories/expense_categories-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,546 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/expense_categories/expense_categoriesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/expense_categories/expense_categoriesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const ExpenseCategoriesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Expense Category"
|
||||
pluralLabel="Expense Categories"
|
||||
stateKey="expense_categories"
|
||||
recordKey="expense_categories"
|
||||
fetchRecord={fetch}
|
||||
listHref="/expense_categories/expense_categories-list"
|
||||
editHref={(id) => `/expense_categories/expense_categories-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
ExpenseCategoriesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_EXPENSE_CATEGORIES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Expense_categoriesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { expense_categories } = useAppSelector((state) => state.expense_categories)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View expense_categories')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View expense_categories')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/expense_categories/expense_categories-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{expense_categories?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ExpenseCategory</p>
|
||||
<p>{expense_categories?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Code</p>
|
||||
<p>{expense_categories?.code}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={expense_categories?.description} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{expense_categories?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Invoices ExpenseCategory</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>InvoiceNumber</th>
|
||||
|
||||
|
||||
|
||||
<th>InvoiceDate</th>
|
||||
|
||||
|
||||
|
||||
<th>ReceivedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>DueDate</th>
|
||||
|
||||
|
||||
|
||||
<th>SubtotalAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>TaxAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>TotalAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>Notes</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{expense_categories.invoices_expense_category && Array.isArray(expense_categories.invoices_expense_category) &&
|
||||
expense_categories.invoices_expense_category.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/invoices/invoices-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="invoice_number">
|
||||
{ item.invoice_number }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="invoice_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.invoice_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="received_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.received_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="due_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.due_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="subtotal_amount">
|
||||
{ item.subtotal_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="tax_amount">
|
||||
{ item.tax_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="total_amount">
|
||||
{ item.total_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="notes">
|
||||
{ item.notes }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!expense_categories?.invoices_expense_category?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/expense_categories/expense_categories-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpenseCategoriesView;
|
||||
Expense_categoriesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_EXPENSE_CATEGORIES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Expense_categoriesView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -372,14 +371,29 @@ const EditField_verificationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Field Verification"
|
||||
pluralLabel="Field Verifications"
|
||||
listHref="/field_verifications/field_verifications-list"
|
||||
viewHref={`/field_verifications/field_verifications-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Project' labelFor='project'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Project' labelFor='project'>
|
||||
<Field
|
||||
name='project'
|
||||
id='project'
|
||||
@ -1074,7 +1088,6 @@ const EditField_verificationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/field_verifications/field_verifications-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,790 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/field_verifications/field_verificationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/field_verifications/field_verificationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const FieldVerificationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Field Verification"
|
||||
pluralLabel="Field Verifications"
|
||||
stateKey="field_verifications"
|
||||
recordKey="field_verifications"
|
||||
fetchRecord={fetch}
|
||||
listHref="/field_verifications/field_verifications-list"
|
||||
editHref={(id) => `/field_verifications/field_verifications-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
FieldVerificationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_FIELD_VERIFICATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Field_verificationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { field_verifications } = useAppSelector((state) => state.field_verifications)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View field_verifications')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View field_verifications')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/field_verifications/field_verifications-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Project</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{field_verifications?.project?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>VerifiedBy</p>
|
||||
|
||||
|
||||
<p>{field_verifications?.verified_by_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='VisitDate'>
|
||||
{field_verifications.visit_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={field_verifications.visit_date ?
|
||||
new Date(
|
||||
dayjs(field_verifications.visit_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No VisitDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>VerificationType</p>
|
||||
<p>{field_verifications?.verification_type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Outcome</p>
|
||||
<p>{field_verifications?.outcome ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Findings</p>
|
||||
{field_verifications.findings
|
||||
? <p dangerouslySetInnerHTML={{__html: field_verifications.findings}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={field_verifications?.actions_required} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='NextFollow-upAt'>
|
||||
{field_verifications.next_followup_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={field_verifications.next_followup_at ?
|
||||
new Date(
|
||||
dayjs(field_verifications.next_followup_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No NextFollow-upAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Photos</p>
|
||||
{field_verifications?.photos?.length
|
||||
? (
|
||||
<ImageField
|
||||
name={'photos'}
|
||||
image={field_verifications?.photos}
|
||||
className='w-20 h-20'
|
||||
/>
|
||||
) : <p>No Photos</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{field_verifications?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/field_verifications/field_verifications-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FieldVerificationsView;
|
||||
Field_verificationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_FIELD_VERIFICATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Field_verificationsView;
|
||||
@ -1,7 +0,0 @@
|
||||
import ExecutiveSummaryPage from './executive-summary';
|
||||
|
||||
const FinancialControlPage: any = ExecutiveSummaryPage;
|
||||
|
||||
FinancialControlPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
|
||||
|
||||
export default FinancialControlPage;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -260,14 +259,28 @@ const EditFiscal_yearsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Fiscal Year"
|
||||
pluralLabel="Fiscal Years"
|
||||
listHref="/fiscal_years/fiscal_years-list"
|
||||
viewHref={`/fiscal_years/fiscal_years-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -596,7 +609,6 @@ const EditFiscal_yearsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/fiscal_years/fiscal_years-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,964 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/fiscal_years/fiscal_yearsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/fiscal_years/fiscal_yearsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const FiscalYearsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Fiscal Year"
|
||||
pluralLabel="Fiscal Years"
|
||||
stateKey="fiscal_years"
|
||||
recordKey="fiscal_years"
|
||||
fetchRecord={fetch}
|
||||
listHref="/fiscal_years/fiscal_years-list"
|
||||
editHref={(id) => `/fiscal_years/fiscal_years-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
FiscalYearsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_FISCAL_YEARS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Fiscal_yearsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { fiscal_years } = useAppSelector((state) => state.fiscal_years)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View fiscal_years')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View fiscal_years')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/fiscal_years/fiscal_years-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{fiscal_years?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FiscalYear</p>
|
||||
<p>{fiscal_years?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='StartDate'>
|
||||
{fiscal_years.start_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={fiscal_years.start_date ?
|
||||
new Date(
|
||||
dayjs(fiscal_years.start_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No StartDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='EndDate'>
|
||||
{fiscal_years.end_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={fiscal_years.end_date ?
|
||||
new Date(
|
||||
dayjs(fiscal_years.end_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No EndDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{fiscal_years?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='IsCurrent'>
|
||||
<SwitchField
|
||||
field={{name: 'is_current', value: fiscal_years?.is_current}}
|
||||
form={{setFieldValue: () => null}}
|
||||
disabled
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Budget_programs FiscalYear</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ProgramName</th>
|
||||
|
||||
|
||||
|
||||
<th>ProgramCode</th>
|
||||
|
||||
|
||||
|
||||
<th>Objective</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>ApprovedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fiscal_years.budget_programs_fiscal_year && Array.isArray(fiscal_years.budget_programs_fiscal_year) &&
|
||||
fiscal_years.budget_programs_fiscal_year.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/budget_programs/budget_programs-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="code">
|
||||
{ item.code }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="objective">
|
||||
{ item.objective }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="approved_amount">
|
||||
{ item.approved_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!fiscal_years?.budget_programs_fiscal_year?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Budget_reallocations FiscalYear</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>Amount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>RequestedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>Justification</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fiscal_years.budget_reallocations_fiscal_year && Array.isArray(fiscal_years.budget_reallocations_fiscal_year) &&
|
||||
fiscal_years.budget_reallocations_fiscal_year.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/budget_reallocations/budget_reallocations-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="amount">
|
||||
{ item.amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="requested_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.requested_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="justification">
|
||||
{ item.justification }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!fiscal_years?.budget_reallocations_fiscal_year?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Procurement_plans FiscalYear</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>PlanName</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>EstimatedTotalAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>SubmittedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>ApprovedAt</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fiscal_years.procurement_plans_fiscal_year && Array.isArray(fiscal_years.procurement_plans_fiscal_year) &&
|
||||
fiscal_years.procurement_plans_fiscal_year.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/procurement_plans/procurement_plans-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="estimated_total_amount">
|
||||
{ item.estimated_total_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="submitted_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.submitted_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="approved_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.approved_at) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!fiscal_years?.procurement_plans_fiscal_year?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Programs FiscalYear</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ProgramName</th>
|
||||
|
||||
|
||||
|
||||
<th>ProgramCode</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>BudgetAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fiscal_years.programs_fiscal_year && Array.isArray(fiscal_years.programs_fiscal_year) &&
|
||||
fiscal_years.programs_fiscal_year.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/programs/programs-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="code">
|
||||
{ item.code }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="budget_amount">
|
||||
{ item.budget_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!fiscal_years?.programs_fiscal_year?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Ledger_entries FiscalYear</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>EntryReference</th>
|
||||
|
||||
|
||||
|
||||
<th>EntryDate</th>
|
||||
|
||||
|
||||
|
||||
<th>EntryType</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>DebitAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>CreditAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Description</th>
|
||||
|
||||
|
||||
|
||||
<th>RecordType</th>
|
||||
|
||||
|
||||
|
||||
<th>RecordKey</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fiscal_years.ledger_entries_fiscal_year && Array.isArray(fiscal_years.ledger_entries_fiscal_year) &&
|
||||
fiscal_years.ledger_entries_fiscal_year.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/ledger_entries/ledger_entries-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="entry_reference">
|
||||
{ item.entry_reference }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="entry_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.entry_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="entry_type">
|
||||
{ item.entry_type }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="debit_amount">
|
||||
{ item.debit_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="credit_amount">
|
||||
{ item.credit_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="description">
|
||||
{ item.description }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="record_type">
|
||||
{ item.record_type }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="record_key">
|
||||
{ item.record_key }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!fiscal_years?.ledger_entries_fiscal_year?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/fiscal_years/fiscal_years-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FiscalYearsView;
|
||||
Fiscal_yearsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_FISCAL_YEARS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Fiscal_yearsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -316,14 +315,28 @@ const EditFunding_sourcesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Funding Source"
|
||||
pluralLabel="Funding Sources"
|
||||
listHref="/funding_sources/funding_sources-list"
|
||||
viewHref={`/funding_sources/funding_sources-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -728,7 +741,6 @@ const EditFunding_sourcesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/funding_sources/funding_sources-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,696 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/funding_sources/funding_sourcesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/funding_sources/funding_sourcesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const FundingSourcesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Funding Source"
|
||||
pluralLabel="Funding Sources"
|
||||
stateKey="funding_sources"
|
||||
recordKey="funding_sources"
|
||||
fetchRecord={fetch}
|
||||
listHref="/funding_sources/funding_sources-list"
|
||||
editHref={(id) => `/funding_sources/funding_sources-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
FundingSourcesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_FUNDING_SOURCES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Funding_sourcesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { funding_sources } = useAppSelector((state) => state.funding_sources)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View funding_sources')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View funding_sources')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/funding_sources/funding_sources-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{funding_sources?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FundingSourceName</p>
|
||||
<p>{funding_sources?.name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>SourceType</p>
|
||||
<p>{funding_sources?.source_type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ReferenceCode</p>
|
||||
<p>{funding_sources?.reference_code}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{funding_sources?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>TotalCommittedAmount</p>
|
||||
<p>{funding_sources?.total_committed_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='EffectiveDate'>
|
||||
{funding_sources.effective_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={funding_sources.effective_date ?
|
||||
new Date(
|
||||
dayjs(funding_sources.effective_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No EffectiveDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{funding_sources?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Budget_programs FundingSource</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ProgramName</th>
|
||||
|
||||
|
||||
|
||||
<th>ProgramCode</th>
|
||||
|
||||
|
||||
|
||||
<th>Objective</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>ApprovedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{funding_sources.budget_programs_funding_source && Array.isArray(funding_sources.budget_programs_funding_source) &&
|
||||
funding_sources.budget_programs_funding_source.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/budget_programs/budget_programs-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="code">
|
||||
{ item.code }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="objective">
|
||||
{ item.objective }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="approved_amount">
|
||||
{ item.approved_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!funding_sources?.budget_programs_funding_source?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Programs FundingSource</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ProgramName</th>
|
||||
|
||||
|
||||
|
||||
<th>ProgramCode</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>BudgetAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{funding_sources.programs_funding_source && Array.isArray(funding_sources.programs_funding_source) &&
|
||||
funding_sources.programs_funding_source.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/programs/programs-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="code">
|
||||
{ item.code }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="budget_amount">
|
||||
{ item.budget_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!funding_sources?.programs_funding_source?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/funding_sources/funding_sources-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FundingSourcesView;
|
||||
Funding_sourcesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_FUNDING_SOURCES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Funding_sourcesView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,29 @@ const EditGrant_applicationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Grant Application"
|
||||
pluralLabel="Grant Applications"
|
||||
listHref="/grant_applications/grant_applications-list"
|
||||
viewHref={`/grant_applications/grant_applications-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Grant' labelFor='grant'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Grant' labelFor='grant'>
|
||||
<Field
|
||||
name='grant'
|
||||
id='grant'
|
||||
@ -1006,7 +1020,6 @@ const EditGrant_applicationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/grant_applications/grant_applications-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,919 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/grant_applications/grant_applicationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/grant_applications/grant_applicationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const GrantApplicationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Grant Application"
|
||||
pluralLabel="Grant Applications"
|
||||
stateKey="grant_applications"
|
||||
recordKey="grant_applications"
|
||||
fetchRecord={fetch}
|
||||
listHref="/grant_applications/grant_applications-list"
|
||||
editHref={(id) => `/grant_applications/grant_applications-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
GrantApplicationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_GRANT_APPLICATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Grant_applicationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { grant_applications } = useAppSelector((state) => state.grant_applications)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View grant_applications')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View grant_applications')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/grant_applications/grant_applications-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Grant</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grant_applications?.grant?.call_reference ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Beneficiary</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grant_applications?.beneficiary?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ApplicationReference</p>
|
||||
<p>{grant_applications?.application_reference}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='SubmittedAt'>
|
||||
{grant_applications.submitted_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={grant_applications.submitted_at ?
|
||||
new Date(
|
||||
dayjs(grant_applications.submitted_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No SubmittedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RequestedAmount</p>
|
||||
<p>{grant_applications?.requested_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{grant_applications?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ProposalSummary</p>
|
||||
{grant_applications.proposal_summary
|
||||
? <p dangerouslySetInnerHTML={{__html: grant_applications.proposal_summary}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{grant_applications?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grant_applications?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Grant_evaluations Application</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>Score</th>
|
||||
|
||||
|
||||
|
||||
<th>Comments</th>
|
||||
|
||||
|
||||
|
||||
<th>Recommendation</th>
|
||||
|
||||
|
||||
|
||||
<th>EvaluatedAt</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{grant_applications.grant_evaluations_application && Array.isArray(grant_applications.grant_evaluations_application) &&
|
||||
grant_applications.grant_evaluations_application.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/grant_evaluations/grant_evaluations-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="score">
|
||||
{ item.score }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="comments">
|
||||
{ item.comments }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="recommendation">
|
||||
{ item.recommendation }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="evaluated_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.evaluated_at) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!grant_applications?.grant_evaluations_application?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Grant_tranches Application</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<th>TrancheNumber</th>
|
||||
|
||||
|
||||
|
||||
<th>Amount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>PlannedDisbursementAt</th>
|
||||
|
||||
|
||||
|
||||
<th>DisbursedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>Conditions</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{grant_applications.grant_tranches_application && Array.isArray(grant_applications.grant_tranches_application) &&
|
||||
grant_applications.grant_tranches_application.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/grant_tranches/grant_tranches-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="tranche_number">
|
||||
{ item.tranche_number }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="amount">
|
||||
{ item.amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="planned_disbursement_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.planned_disbursement_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="disbursed_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.disbursed_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="conditions">
|
||||
{ item.conditions }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!grant_applications?.grant_tranches_application?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/grant_applications/grant_applications-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GrantApplicationsView;
|
||||
Grant_applicationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_GRANT_APPLICATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Grant_applicationsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -288,14 +287,29 @@ const EditGrant_evaluationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Grant Evaluation"
|
||||
pluralLabel="Grant Evaluations"
|
||||
listHref="/grant_evaluations/grant_evaluations-list"
|
||||
viewHref={`/grant_evaluations/grant_evaluations-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Application' labelFor='application'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Application' labelFor='application'>
|
||||
<Field
|
||||
name='application'
|
||||
id='application'
|
||||
@ -861,7 +875,6 @@ const EditGrant_evaluationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/grant_evaluations/grant_evaluations-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,675 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/grant_evaluations/grant_evaluationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/grant_evaluations/grant_evaluationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const GrantEvaluationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Grant Evaluation"
|
||||
pluralLabel="Grant Evaluations"
|
||||
stateKey="grant_evaluations"
|
||||
recordKey="grant_evaluations"
|
||||
fetchRecord={fetch}
|
||||
listHref="/grant_evaluations/grant_evaluations-list"
|
||||
editHref={(id) => `/grant_evaluations/grant_evaluations-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
GrantEvaluationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_GRANT_EVALUATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Grant_evaluationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { grant_evaluations } = useAppSelector((state) => state.grant_evaluations)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View grant_evaluations')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View grant_evaluations')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/grant_evaluations/grant_evaluations-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Application</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grant_evaluations?.application?.application_reference ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Evaluator</p>
|
||||
|
||||
|
||||
<p>{grant_evaluations?.evaluator_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Score</p>
|
||||
<p>{grant_evaluations?.score || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={grant_evaluations?.comments} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Recommendation</p>
|
||||
<p>{grant_evaluations?.recommendation ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='EvaluatedAt'>
|
||||
{grant_evaluations.evaluated_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={grant_evaluations.evaluated_at ?
|
||||
new Date(
|
||||
dayjs(grant_evaluations.evaluated_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No EvaluatedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grant_evaluations?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/grant_evaluations/grant_evaluations-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GrantEvaluationsView;
|
||||
Grant_evaluationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_GRANT_EVALUATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Grant_evaluationsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,29 @@ const EditGrant_tranchesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Grant Tranche"
|
||||
pluralLabel="Grant Tranches"
|
||||
listHref="/grant_tranches/grant_tranches-list"
|
||||
viewHref={`/grant_tranches/grant_tranches-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Application' labelFor='application'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Application' labelFor='application'>
|
||||
<Field
|
||||
name='application'
|
||||
id='application'
|
||||
@ -900,7 +914,6 @@ const EditGrant_tranchesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/grant_tranches/grant_tranches-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,645 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/grant_tranches/grant_tranchesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/grant_tranches/grant_tranchesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const GrantTranchesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Grant Tranche"
|
||||
pluralLabel="Grant Tranches"
|
||||
stateKey="grant_tranches"
|
||||
recordKey="grant_tranches"
|
||||
fetchRecord={fetch}
|
||||
listHref="/grant_tranches/grant_tranches-list"
|
||||
editHref={(id) => `/grant_tranches/grant_tranches-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
GrantTranchesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_GRANT_TRANCHES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Grant_tranchesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { grant_tranches } = useAppSelector((state) => state.grant_tranches)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View grant_tranches')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View grant_tranches')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/grant_tranches/grant_tranches-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Application</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grant_tranches?.application?.application_reference ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>TrancheNumber</p>
|
||||
<p>{grant_tranches?.tranche_number || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Amount</p>
|
||||
<p>{grant_tranches?.amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{grant_tranches?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='PlannedDisbursementAt'>
|
||||
{grant_tranches.planned_disbursement_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={grant_tranches.planned_disbursement_at ?
|
||||
new Date(
|
||||
dayjs(grant_tranches.planned_disbursement_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No PlannedDisbursementAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='DisbursedAt'>
|
||||
{grant_tranches.disbursed_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={grant_tranches.disbursed_at ?
|
||||
new Date(
|
||||
dayjs(grant_tranches.disbursed_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No DisbursedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{grant_tranches?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={grant_tranches?.conditions} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grant_tranches?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/grant_tranches/grant_tranches-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GrantTranchesView;
|
||||
Grant_tranchesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_GRANT_TRANCHES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Grant_tranchesView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -400,14 +399,28 @@ const EditGrantsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Grant"
|
||||
pluralLabel="Grants"
|
||||
listHref="/grants/grants-list"
|
||||
viewHref={`/grants/grants-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1033,7 +1046,6 @@ const EditGrantsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/grants/grants-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,926 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/grants/grantsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/grants/grantsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const GrantsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Grant"
|
||||
pluralLabel="Grants"
|
||||
stateKey="grants"
|
||||
recordKey="grants"
|
||||
fetchRecord={fetch}
|
||||
listHref="/grants/grants-list"
|
||||
editHref={(id) => `/grants/grants-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
GrantsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_GRANTS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const GrantsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { grants } = useAppSelector((state) => state.grants)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View grants')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View grants')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/grants/grants-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grants?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Program</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{grants?.program?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FundingWindowName</p>
|
||||
<p>{grants?.funding_window_name}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>CallReference</p>
|
||||
<p>{grants?.call_reference}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='CallOpenAt'>
|
||||
{grants.call_open_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={grants.call_open_at ?
|
||||
new Date(
|
||||
dayjs(grants.call_open_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No CallOpenAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='CallCloseAt'>
|
||||
{grants.call_close_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={grants.call_close_at ?
|
||||
new Date(
|
||||
dayjs(grants.call_close_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No CallCloseAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{grants?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>EligibilityRules</p>
|
||||
{grants.eligibility_rules
|
||||
? <p dangerouslySetInnerHTML={{__html: grants.eligibility_rules}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>TotalEnvelopeAmount</p>
|
||||
<p>{grants?.total_envelope_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{grants?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={grants?.notes} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Beneficiaries Grant</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>BeneficiaryName</th>
|
||||
|
||||
|
||||
|
||||
<th>BeneficiaryType</th>
|
||||
|
||||
|
||||
|
||||
<th>RegistrationNumber</th>
|
||||
|
||||
|
||||
|
||||
<th>ContactEmail</th>
|
||||
|
||||
|
||||
|
||||
<th>ContactPhone</th>
|
||||
|
||||
|
||||
|
||||
<th>ApprovedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>ApprovedAt</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{grants.beneficiaries_grant && Array.isArray(grants.beneficiaries_grant) &&
|
||||
grants.beneficiaries_grant.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/beneficiaries/beneficiaries-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="name">
|
||||
{ item.name }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="beneficiary_type">
|
||||
{ item.beneficiary_type }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="registration_number">
|
||||
{ item.registration_number }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="contact_email">
|
||||
{ item.contact_email }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="contact_phone">
|
||||
{ item.contact_phone }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="approved_amount">
|
||||
{ item.approved_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="approved_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.approved_at) }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!grants?.beneficiaries_grant?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Grant_applications Grant</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>ApplicationReference</th>
|
||||
|
||||
|
||||
|
||||
<th>SubmittedAt</th>
|
||||
|
||||
|
||||
|
||||
<th>RequestedAmount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{grants.grant_applications_grant && Array.isArray(grants.grant_applications_grant) &&
|
||||
grants.grant_applications_grant.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/grant_applications/grant_applications-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="application_reference">
|
||||
{ item.application_reference }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="submitted_at">
|
||||
{ dataFormatter.dateTimeFormatter(item.submitted_at) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="requested_amount">
|
||||
{ item.requested_amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!grants?.grant_applications_grant?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/grants/grants-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GrantsView;
|
||||
GrantsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_GRANTS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default GrantsView;
|
||||
@ -1,222 +1,166 @@
|
||||
import {
|
||||
mdiBankOutline,
|
||||
mdiCheckDecagramOutline,
|
||||
mdiClipboardClockOutline,
|
||||
mdiFileDocumentOutline,
|
||||
mdiLogin,
|
||||
mdiMapMarkerRadiusOutline,
|
||||
mdiOfficeBuildingCogOutline,
|
||||
mdiShieldCheckOutline,
|
||||
mdiWalletOutline,
|
||||
} from '@mdi/js';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import BaseButton from '../components/BaseButton';
|
||||
import BaseDivider from '../components/BaseDivider';
|
||||
import BaseIcon from '../components/BaseIcon';
|
||||
import CardBox from '../components/CardBox';
|
||||
import { getPageTitle } from '../config';
|
||||
import { getLoginRoute, getWorkspaceRoute } from '../helpers/workspace';
|
||||
import SectionFullScreen from '../components/SectionFullScreen';
|
||||
import LayoutGuest from '../layouts/Guest';
|
||||
import { findMe } from '../stores/authSlice';
|
||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||
import BaseDivider from '../components/BaseDivider';
|
||||
import BaseButtons from '../components/BaseButtons';
|
||||
import { getPageTitle } from '../config';
|
||||
import { useAppSelector } from '../stores/hooks';
|
||||
import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
|
||||
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
|
||||
|
||||
const moduleCards = [
|
||||
{
|
||||
title: 'Budgeting and fund allocation',
|
||||
description: 'Approved allocations, commitment exposure, reallocations, and fiscal-year control across funding programs.',
|
||||
icon: mdiBankOutline,
|
||||
},
|
||||
{
|
||||
title: 'Procurement and approvals',
|
||||
description: 'Requisition-to-award workflow with internal approvals, tender visibility, and contract handoff.',
|
||||
icon: mdiClipboardClockOutline,
|
||||
},
|
||||
{
|
||||
title: 'Contracts and vendors',
|
||||
description: 'Vendor qualification, compliance review, contract expiry monitoring, and payment readiness.',
|
||||
icon: mdiFileDocumentOutline,
|
||||
},
|
||||
{
|
||||
title: 'Projects and rollout oversight',
|
||||
description: 'Province-level implementation tracking for school broadband, digital centers, rural connectivity, and ICT programs.',
|
||||
icon: mdiMapMarkerRadiusOutline,
|
||||
},
|
||||
];
|
||||
|
||||
const controlPoints = [
|
||||
'Approval-aware procurement workflow from requisition to payment',
|
||||
'Vendor compliance, document expiry, and audit-ready record trails',
|
||||
'Executive summary with budget, contract, project, and risk visibility',
|
||||
'Operational links into the existing admin interface and CRUD work areas',
|
||||
];
|
||||
export default function Starter() {
|
||||
const [illustrationImage, setIllustrationImage] = useState({
|
||||
src: undefined,
|
||||
photographer: undefined,
|
||||
photographer_url: undefined,
|
||||
})
|
||||
const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
|
||||
const [contentType, setContentType] = useState('image');
|
||||
const [contentPosition, setContentPosition] = useState('background');
|
||||
const textColor = useAppSelector((state) => state.style.linkColor);
|
||||
|
||||
export default function HomePage() {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const { currentUser, token } = useAppSelector((state) => state.auth);
|
||||
const title = 'FDSU ERP'
|
||||
|
||||
const workspaceRoute = useMemo(
|
||||
() => getWorkspaceRoute(currentUser?.app_role?.name),
|
||||
[currentUser?.app_role?.name],
|
||||
);
|
||||
// Fetch Pexels image/video
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
const image = await getPexelsImage();
|
||||
const video = await getPexelsVideo();
|
||||
setIllustrationImage(image);
|
||||
setIllustrationVideo(video);
|
||||
}
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const existingToken = token || (typeof window !== 'undefined' ? localStorage.getItem('token') : '');
|
||||
const imageBlock = (image) => (
|
||||
<div
|
||||
className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'
|
||||
style={{
|
||||
backgroundImage: `${
|
||||
image
|
||||
? `url(${image?.src?.original})`
|
||||
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
||||
}`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'left center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
>
|
||||
<div className='flex justify-center w-full bg-blue-300/20'>
|
||||
<a
|
||||
className='text-[8px]'
|
||||
href={image?.photographer_url}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
>
|
||||
Photo by {image?.photographer} on Pexels
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (existingToken && !currentUser?.id) {
|
||||
dispatch(findMe());
|
||||
}
|
||||
}, [currentUser?.id, dispatch, token]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUser?.id) {
|
||||
router.replace(workspaceRoute);
|
||||
}
|
||||
}, [currentUser?.id, router, workspaceRoute]);
|
||||
|
||||
const getProtectedHref = (href: string) => (currentUser?.id ? href : getLoginRoute(href));
|
||||
|
||||
const primaryWorkspaceHref = currentUser?.id ? workspaceRoute : getLoginRoute('/executive-summary');
|
||||
const dashboardHref = currentUser?.id ? '/dashboard' : getLoginRoute('/dashboard');
|
||||
const videoBlock = (video) => {
|
||||
if (video?.video_files?.length > 0) {
|
||||
return (
|
||||
<div className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'>
|
||||
<video
|
||||
className='absolute top-0 left-0 w-full h-full object-cover'
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
>
|
||||
<source src={video?.video_files[0]?.link} type='video/mp4'/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<div className='flex justify-center w-full bg-blue-300/20 z-10'>
|
||||
<a
|
||||
className='text-[8px]'
|
||||
href={video?.user?.url}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
>
|
||||
Video by {video.user.name} on Pexels
|
||||
</a>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={
|
||||
contentPosition === 'background'
|
||||
? {
|
||||
backgroundImage: `${
|
||||
illustrationImage
|
||||
? `url(${illustrationImage.src?.original})`
|
||||
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
||||
}`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'left center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<Head>
|
||||
<title>{getPageTitle('FDSU ERP')}</title>
|
||||
<title>{getPageTitle('Starter Page')}</title>
|
||||
</Head>
|
||||
|
||||
<div className='min-h-screen bg-slate-100 text-slate-900'>
|
||||
<header className='border-b border-slate-200 bg-white'>
|
||||
<div className='mx-auto flex max-w-7xl items-center justify-between gap-4 px-6 py-4'>
|
||||
<div>
|
||||
<p className='text-xs font-semibold uppercase tracking-[0.24em] text-slate-500'>National public fund authority platform</p>
|
||||
<h1 className='mt-1 text-2xl font-semibold tracking-tight text-slate-950'>FDSU ERP</h1>
|
||||
<SectionFullScreen bg='violet'>
|
||||
<div
|
||||
className={`flex ${
|
||||
contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'
|
||||
} min-h-screen w-full`}
|
||||
>
|
||||
{contentType === 'image' && contentPosition !== 'background'
|
||||
? imageBlock(illustrationImage)
|
||||
: null}
|
||||
{contentType === 'video' && contentPosition !== 'background'
|
||||
? videoBlock(illustrationVideo)
|
||||
: null}
|
||||
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
||||
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
|
||||
<CardBoxComponentTitle title="Welcome to your FDSU ERP app!"/>
|
||||
|
||||
<div className="space-y-3">
|
||||
<p className='text-center text-gray-500'>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
|
||||
<p className='text-center text-gray-500'>For guides and documentation please check
|
||||
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
|
||||
</div>
|
||||
<div className='flex flex-wrap items-center gap-3'>
|
||||
<BaseButton href={currentUser?.id ? workspaceRoute : '/login'} color='whiteDark' icon={mdiLogin} label={currentUser?.id ? 'My workspace' : 'Login'} />
|
||||
<BaseButton href={dashboardHref} color='info' icon={mdiOfficeBuildingCogOutline} label='Admin interface' />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<BaseButtons>
|
||||
<BaseButton
|
||||
href='/login'
|
||||
label='Login'
|
||||
color='info'
|
||||
className='w-full'
|
||||
/>
|
||||
|
||||
<main className='mx-auto max-w-7xl px-6 py-8'>
|
||||
<section className='grid gap-6 xl:grid-cols-[1.55fr,1fr]'>
|
||||
<CardBox className='border border-slate-200 bg-slate-950 text-slate-100'>
|
||||
<div className='max-w-4xl'>
|
||||
<p className='text-xs font-semibold uppercase tracking-[0.24em] text-slate-400'>Institutional ERP foundation</p>
|
||||
<h2 className='mt-4 text-4xl font-semibold tracking-tight text-white'>
|
||||
Operational control for budgeting, procurement, contract management, compliance, and digital inclusion rollout.
|
||||
</h2>
|
||||
<p className='mt-5 max-w-3xl text-base leading-7 text-slate-300'>
|
||||
FDSU ERP is designed as a serious working environment for finance, procurement, project delivery, internal control, audit, and executive leadership.
|
||||
It centralizes recordkeeping, approvals, payment oversight, vendor qualification, contract obligations, and province-level implementation in one platform.
|
||||
</p>
|
||||
|
||||
<div className='mt-8 flex flex-wrap gap-3'>
|
||||
<BaseButton href={primaryWorkspaceHref} color='info' label={currentUser?.id ? 'Open my workspace' : 'Open executive summary'} />
|
||||
<BaseButton href={getProtectedHref('/requisitions/requisitions-new')} color='whiteDark' label='Create requisition' />
|
||||
<BaseButton href={getProtectedHref('/approvals/approvals-list')} color='whiteDark' label='Open approval inbox' />
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<CardBox className='border border-slate-200 bg-white'>
|
||||
<p className='text-xs font-semibold uppercase tracking-[0.24em] text-slate-500'>What the first delivery includes</p>
|
||||
<div className='mt-5 space-y-3'>
|
||||
{controlPoints.map((item) => (
|
||||
<div key={item} className='flex items-start gap-3 rounded-md border border-slate-200 bg-slate-50 px-4 py-3'>
|
||||
<span className='mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-slate-900 text-white'>
|
||||
<BaseIcon path={mdiShieldCheckOutline} size={14} />
|
||||
</span>
|
||||
<p className='text-sm leading-6 text-slate-700'>{item}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<BaseDivider />
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<div className='rounded-md border border-slate-200 p-4'>
|
||||
<p className='text-xs uppercase tracking-[0.18em] text-slate-400'>Interface style</p>
|
||||
<p className='mt-2 text-sm font-medium text-slate-900'>Dense, audit-conscious, line-of-business layout</p>
|
||||
</div>
|
||||
<div className='rounded-md border border-slate-200 p-4'>
|
||||
<p className='text-xs uppercase tracking-[0.18em] text-slate-400'>Access</p>
|
||||
<p className='mt-2 text-sm font-medium text-slate-900'>Public landing with role-aware sign-in routing into the secured workspace</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
</section>
|
||||
|
||||
<section className='mt-8 grid gap-4 lg:grid-cols-2 xl:grid-cols-4'>
|
||||
{moduleCards.map((card) => (
|
||||
<CardBox key={card.title} className='h-full border border-slate-200 bg-white'>
|
||||
<div className='flex items-start justify-between gap-3'>
|
||||
<div>
|
||||
<p className='text-xs font-semibold uppercase tracking-[0.18em] text-slate-500'>ERP module</p>
|
||||
<h3 className='mt-3 text-lg font-semibold text-slate-900'>{card.title}</h3>
|
||||
<p className='mt-3 text-sm leading-6 text-slate-600'>{card.description}</p>
|
||||
</div>
|
||||
<div className='flex h-11 w-11 items-center justify-center rounded-md border border-slate-200 bg-slate-50'>
|
||||
<BaseIcon path={card.icon} size={20} className='text-slate-700' />
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section className='mt-8 grid gap-6 xl:grid-cols-[1.2fr,1fr]'>
|
||||
<CardBox className='border border-slate-200 bg-white'>
|
||||
<p className='text-xs font-semibold uppercase tracking-[0.24em] text-slate-500'>Operational workflow</p>
|
||||
<h3 className='mt-2 text-2xl font-semibold text-slate-900'>Thin-slice journey now wired into the app</h3>
|
||||
<div className='mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-4'>
|
||||
{[
|
||||
['1', 'Create requisition', 'Capture procurement demand and route it into the formal approval chain.'],
|
||||
['2', 'Review approvals', 'Use the new executive summary and approval inbox to identify pending actions.'],
|
||||
['3', 'Inspect contracts', 'Track contracts nearing expiry and top-value commitments in one place.'],
|
||||
['4', 'Monitor rollout', 'Review project concentration by province and outstanding operational risk.'],
|
||||
].map(([step, title, text]) => (
|
||||
<div key={step} className='rounded-md border border-slate-200 bg-slate-50 p-4'>
|
||||
<div className='flex h-8 w-8 items-center justify-center rounded-full bg-slate-900 text-sm font-semibold text-white'>{step}</div>
|
||||
<p className='mt-4 font-medium text-slate-900'>{title}</p>
|
||||
<p className='mt-2 text-sm leading-6 text-slate-600'>{text}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<CardBox className='border border-slate-200 bg-white'>
|
||||
<p className='text-xs font-semibold uppercase tracking-[0.24em] text-slate-500'>Quick access</p>
|
||||
<div className='mt-5 grid gap-3'>
|
||||
{[
|
||||
{ href: currentUser?.id ? workspaceRoute : getLoginRoute('/executive-summary'), icon: mdiWalletOutline, title: 'Executive summary', text: 'Operational overview, approval queue, risk panel, and rollout indicators.' },
|
||||
{ href: getProtectedHref('/requisitions/requisitions-list'), icon: mdiClipboardClockOutline, title: 'Requisitions', text: 'Create, list, and review procurement requests.' },
|
||||
{ href: getProtectedHref('/contracts/contracts-list'), icon: mdiFileDocumentOutline, title: 'Contracts', text: 'Open the contract register and milestone detail views.' },
|
||||
{ href: getProtectedHref('/vendors/vendors-list'), icon: mdiCheckDecagramOutline, title: 'Vendor master', text: 'Access qualification, banking data, compliance, and related history.' },
|
||||
].map((item) => (
|
||||
<Link key={item.href} href={item.href} className='rounded-md border border-slate-200 p-4 transition hover:border-slate-300 hover:bg-slate-50'>
|
||||
<div className='flex items-start gap-3'>
|
||||
<div className='mt-0.5 flex h-10 w-10 items-center justify-center rounded-md border border-slate-200 bg-slate-50'>
|
||||
<BaseIcon path={item.icon} size={18} className='text-slate-700' />
|
||||
</div>
|
||||
<div>
|
||||
<p className='font-medium text-slate-900'>{item.title}</p>
|
||||
<p className='mt-1 text-sm leading-6 text-slate-600'>{item.text}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</CardBox>
|
||||
</section>
|
||||
</main>
|
||||
</BaseButtons>
|
||||
</CardBox>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</SectionFullScreen>
|
||||
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row'>
|
||||
<p className='py-6 text-sm'>© 2026 <span>{title}</span>. All rights reserved</p>
|
||||
<Link className='py-6 ml-4 text-sm' href='/privacy-policy/'>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
HomePage.getLayout = function getLayout(page: ReactElement) {
|
||||
Starter.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutGuest>{page}</LayoutGuest>;
|
||||
};
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -512,14 +511,28 @@ const EditInvoicesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Invoice"
|
||||
pluralLabel="Invoices"
|
||||
listHref="/invoices/invoices-list"
|
||||
viewHref={`/invoices/invoices-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1615,7 +1628,6 @@ const EditInvoicesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/invoices/invoices-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -372,14 +371,29 @@ const EditIssuesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Issue"
|
||||
pluralLabel="Issues"
|
||||
listHref="/issues/issues-list"
|
||||
viewHref={`/issues/issues-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField label='Project' labelFor='project'>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Project' labelFor='project'>
|
||||
<Field
|
||||
name='project'
|
||||
id='project'
|
||||
@ -1080,7 +1094,6 @@ const EditIssuesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/issues/issues-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,791 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/issues/issuesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/issues/issuesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const IssuesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Issue"
|
||||
pluralLabel="Issues"
|
||||
stateKey="issues"
|
||||
recordKey="issues"
|
||||
fetchRecord={fetch}
|
||||
listHref="/issues/issues-list"
|
||||
editHref={(id) => `/issues/issues-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
IssuesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_ISSUES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const IssuesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { issues } = useAppSelector((state) => state.issues)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View issues')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View issues')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/issues/issues-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Project</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{issues?.project?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>IssueTitle</p>
|
||||
<p>{issues?.title}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Description</p>
|
||||
{issues.description
|
||||
? <p dangerouslySetInnerHTML={{__html: issues.description}}/>
|
||||
: <p>No data</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Severity</p>
|
||||
<p>{issues?.severity ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{issues?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>AssignedTo</p>
|
||||
|
||||
|
||||
<p>{issues?.assigned_to_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='OpenedAt'>
|
||||
{issues.opened_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={issues.opened_at ?
|
||||
new Date(
|
||||
dayjs(issues.opened_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No OpenedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='DueAt'>
|
||||
{issues.due_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={issues.due_at ?
|
||||
new Date(
|
||||
dayjs(issues.due_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No DueAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='ResolvedAt'>
|
||||
{issues.resolved_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={issues.resolved_at ?
|
||||
new Date(
|
||||
dayjs(issues.resolved_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No ResolvedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>organizations</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{issues?.organizations?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/issues/issues-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IssuesView;
|
||||
IssuesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_ISSUES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default IssuesView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -400,14 +399,28 @@ const EditLedger_entriesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Ledger Entry"
|
||||
pluralLabel="Ledger Entries"
|
||||
listHref="/ledger_entries/ledger_entries-list"
|
||||
viewHref={`/ledger_entries/ledger_entries-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1025,7 +1038,6 @@ const EditLedger_entriesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/ledger_entries/ledger_entries-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,701 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/ledger_entries/ledger_entriesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/ledger_entries/ledger_entriesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const LedgerEntriesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Ledger Entry"
|
||||
pluralLabel="Ledger Entries"
|
||||
stateKey="ledger_entries"
|
||||
recordKey="ledger_entries"
|
||||
fetchRecord={fetch}
|
||||
listHref="/ledger_entries/ledger_entries-list"
|
||||
editHref={(id) => `/ledger_entries/ledger_entries-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
LedgerEntriesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_LEDGER_ENTRIES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Ledger_entriesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { ledger_entries } = useAppSelector((state) => state.ledger_entries)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View ledger_entries')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View ledger_entries')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/ledger_entries/ledger_entries-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{ledger_entries?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>FiscalYear</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{ledger_entries?.fiscal_year?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>EntryReference</p>
|
||||
<p>{ledger_entries?.entry_reference}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='EntryDate'>
|
||||
{ledger_entries.entry_date ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={ledger_entries.entry_date ?
|
||||
new Date(
|
||||
dayjs(ledger_entries.entry_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No EntryDate</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>EntryType</p>
|
||||
<p>{ledger_entries?.entry_type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{ledger_entries?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>DebitAmount</p>
|
||||
<p>{ledger_entries?.debit_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>CreditAmount</p>
|
||||
<p>{ledger_entries?.credit_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={ledger_entries?.description} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordType</p>
|
||||
<p>{ledger_entries?.record_type}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordKey</p>
|
||||
<p>{ledger_entries?.record_key}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/ledger_entries/ledger_entries-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LedgerEntriesView;
|
||||
Ledger_entriesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_LEDGER_ENTRIES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Ledger_entriesView;
|
||||
@ -21,17 +21,6 @@ import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||
import Link from 'next/link';
|
||||
import {toast, ToastContainer} from "react-toastify";
|
||||
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'
|
||||
import { getPostLoginRoute } from '../helpers/workspace';
|
||||
|
||||
const roleAccounts = [
|
||||
{ email: 'super_admin@flatlogic.com', password: '5e8f2960', roleLabel: 'Super Administrator' },
|
||||
{ email: 'admin@flatlogic.com', password: '5e8f2960', roleLabel: 'Administrator' },
|
||||
{ email: 'director.general@flatlogic.com', password: '242b5480541a', roleLabel: 'Director General' },
|
||||
{ email: 'finance.director@flatlogic.com', password: '242b5480541a', roleLabel: 'Finance Director' },
|
||||
{ email: 'procurement.lead@flatlogic.com', password: '242b5480541a', roleLabel: 'Procurement Lead' },
|
||||
{ email: 'compliance.audit@flatlogic.com', password: '242b5480541a', roleLabel: 'Compliance and Audit Lead' },
|
||||
{ email: 'project.delivery@flatlogic.com', password: '242b5480541a', roleLabel: 'Project Delivery Lead' },
|
||||
];
|
||||
|
||||
export default function Login() {
|
||||
const router = useRouter();
|
||||
@ -51,11 +40,9 @@ export default function Login() {
|
||||
const { currentUser, isFetching, errorMessage, token, notify:notifyState } = useAppSelector(
|
||||
(state) => state.auth,
|
||||
);
|
||||
const [initialValues, setInitialValues] = React.useState({
|
||||
email: roleAccounts[0].email,
|
||||
password: roleAccounts[0].password,
|
||||
remember: true,
|
||||
})
|
||||
const [initialValues, setInitialValues] = React.useState({ email:'super_admin@flatlogic.com',
|
||||
password: '5e8f2960',
|
||||
remember: true })
|
||||
|
||||
const title = 'FDSU ERP'
|
||||
|
||||
@ -71,18 +58,16 @@ export default function Login() {
|
||||
}, []);
|
||||
// Fetch user data
|
||||
useEffect(() => {
|
||||
const existingToken = token || (typeof window !== 'undefined' ? localStorage.getItem('token') : '');
|
||||
|
||||
if (existingToken) {
|
||||
if (token) {
|
||||
dispatch(findMe());
|
||||
}
|
||||
}, [dispatch, token]);
|
||||
// Redirect to the intended deep link or the role workspace if user is logged in
|
||||
}, [token, dispatch]);
|
||||
// Redirect to dashboard if user is logged in
|
||||
useEffect(() => {
|
||||
if (currentUser?.id) {
|
||||
router.replace(getPostLoginRoute(currentUser?.app_role?.name, router.query.redirect));
|
||||
router.push('/dashboard');
|
||||
}
|
||||
}, [currentUser?.app_role?.name, currentUser?.id, router, router.query.redirect]);
|
||||
}, [currentUser?.id, router]);
|
||||
// Show error message if there is one
|
||||
useEffect(() => {
|
||||
if (errorMessage){
|
||||
@ -182,24 +167,30 @@ export default function Login() {
|
||||
|
||||
<h2 className="text-4xl font-semibold my-4">{title}</h2>
|
||||
|
||||
<div className='flex flex-col gap-4 text-gray-500 md:flex-row md:justify-between'>
|
||||
<div className='space-y-3'>
|
||||
<p className='text-sm text-gray-500'>Role access accounts for this workspace. Click any email to autofill the form.</p>
|
||||
{roleAccounts.map((account) => (
|
||||
<p key={account.email}>
|
||||
<span className='font-medium text-gray-700 dark:text-gray-200'>{account.roleLabel}:</span>{' '}
|
||||
<code
|
||||
className={`cursor-pointer ${textColor}`}
|
||||
data-password={account.password}
|
||||
onClick={(e) => setLogin(e.target as HTMLElement)}
|
||||
>
|
||||
{account.email}
|
||||
</code>{' / '}
|
||||
<code className={`${textColor}`}>{account.password}</code>
|
||||
</p>
|
||||
))}
|
||||
<div className='flex flex-row text-gray-500 justify-between'>
|
||||
<div>
|
||||
|
||||
<p className='mb-2'>Use{' '}
|
||||
<code className={`cursor-pointer ${textColor} `}
|
||||
data-password="5e8f2960"
|
||||
onClick={(e) => setLogin(e.target)}>super_admin@flatlogic.com</code>{' / '}
|
||||
<code className={`${textColor}`}>5e8f2960</code>{' / '}
|
||||
to login as Super Admin</p>
|
||||
|
||||
<p className='mb-2'>Use{' '}
|
||||
<code className={`cursor-pointer ${textColor} `}
|
||||
data-password="5e8f2960"
|
||||
onClick={(e) => setLogin(e.target)}>admin@flatlogic.com</code>{' / '}
|
||||
<code className={`${textColor}`}>5e8f2960</code>{' / '}
|
||||
to login as Admin</p>
|
||||
<p>Use <code
|
||||
className={`cursor-pointer ${textColor} `}
|
||||
data-password="242b5480541a"
|
||||
onClick={(e) => setLogin(e.target)}>client@hello.com</code>{' / '}
|
||||
<code className={`${textColor}`}>242b5480541a</code>{' / '}
|
||||
to login as User</p>
|
||||
</div>
|
||||
<div className='flex justify-end md:block'>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w='w-16'
|
||||
@ -220,7 +211,7 @@ export default function Login() {
|
||||
<Form>
|
||||
<FormField
|
||||
label='Login'
|
||||
help='Please enter your email address'>
|
||||
help='Please enter your login'>
|
||||
<Field name='email' />
|
||||
</FormField>
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -344,14 +343,28 @@ const EditNotificationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Notification"
|
||||
pluralLabel="Notifications"
|
||||
listHref="/notifications/notifications-list"
|
||||
viewHref={`/notifications/notifications-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -897,7 +910,6 @@ const EditNotificationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/notifications/notifications-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,640 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/notifications/notificationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/notifications/notificationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const NotificationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Notification"
|
||||
pluralLabel="Notifications"
|
||||
stateKey="notifications"
|
||||
recordKey="notifications"
|
||||
fetchRecord={fetch}
|
||||
listHref="/notifications/notifications-list"
|
||||
editHref={(id) => `/notifications/notifications-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
NotificationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_NOTIFICATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const NotificationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { notifications } = useAppSelector((state) => state.notifications)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View notifications')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View notifications')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/notifications/notifications-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{notifications?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>User</p>
|
||||
|
||||
|
||||
<p>{notifications?.user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Type</p>
|
||||
<p>{notifications?.type ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Title</p>
|
||||
<p>{notifications?.title}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={notifications?.message} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordType</p>
|
||||
<p>{notifications?.record_type}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RecordKey</p>
|
||||
<p>{notifications?.record_key}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Read'>
|
||||
<SwitchField
|
||||
field={{name: 'read', value: notifications?.read}}
|
||||
form={{setFieldValue: () => null}}
|
||||
disabled
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='SentAt'>
|
||||
{notifications.sent_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={notifications.sent_at ?
|
||||
new Date(
|
||||
dayjs(notifications.sent_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No SentAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/notifications/notifications-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationsView;
|
||||
NotificationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_NOTIFICATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -372,14 +371,28 @@ const EditObligationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Obligation"
|
||||
pluralLabel="Obligations"
|
||||
listHref="/obligations/obligations-list"
|
||||
viewHref={`/obligations/obligations-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1165,7 +1178,6 @@ const EditObligationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/obligations/obligations-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,873 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/obligations/obligationsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/obligations/obligationsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const ObligationsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Obligation"
|
||||
pluralLabel="Obligations"
|
||||
stateKey="obligations"
|
||||
recordKey="obligations"
|
||||
fetchRecord={fetch}
|
||||
listHref="/obligations/obligations-list"
|
||||
editHref={(id) => `/obligations/obligations-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
ObligationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_OBLIGATIONS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const ObligationsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { obligations } = useAppSelector((state) => state.obligations)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View obligations')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View obligations')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/obligations/obligations-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{obligations?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>BudgetLine</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{obligations?.budget_line?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Contract</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{obligations?.contract?.contract_number ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Project</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{obligations?.project?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>ObligationNumber</p>
|
||||
<p>{obligations?.obligation_number}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Amount</p>
|
||||
<p>{obligations?.amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{obligations?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='ObligatedAt'>
|
||||
{obligations.obligated_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={obligations.obligated_at ?
|
||||
new Date(
|
||||
dayjs(obligations.obligated_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No ObligatedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{obligations?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={obligations?.notes} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/obligations/obligations-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ObligationsView;
|
||||
ObligationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_OBLIGATIONS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default ObligationsView;
|
||||
@ -1,7 +0,0 @@
|
||||
import ExecutiveSummaryPage from './executive-summary';
|
||||
|
||||
const OperationsCommandPage: any = ExecutiveSummaryPage;
|
||||
|
||||
OperationsCommandPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
|
||||
|
||||
export default OperationsCommandPage;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -120,14 +119,10 @@ const EditOrganizationsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Organization"
|
||||
pluralLabel="Organizations"
|
||||
listHref="/organizations/organizations-list"
|
||||
viewHref={`/organizations/organizations-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
<FormField
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="Name"
|
||||
>
|
||||
<Field
|
||||
@ -170,7 +165,6 @@ const EditOrganizationsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/organizations/organizations-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -316,14 +315,28 @@ const EditPayment_batchesPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Payment Batch"
|
||||
pluralLabel="Payment Batchs"
|
||||
listHref="/payment_batches/payment_batches-list"
|
||||
viewHref={`/payment_batches/payment_batches-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -840,7 +853,6 @@ const EditPayment_batchesPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/payment_batches/payment_batches-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,717 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/payment_batches/payment_batchesSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/payment_batches/payment_batchesSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const PaymentBatchesView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Payment Batch"
|
||||
pluralLabel="Payment Batchs"
|
||||
stateKey="payment_batches"
|
||||
recordKey="payment_batches"
|
||||
fetchRecord={fetch}
|
||||
listHref="/payment_batches/payment_batches-list"
|
||||
editHref={(id) => `/payment_batches/payment_batches-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
PaymentBatchesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_PAYMENT_BATCHES'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Payment_batchesView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { payment_batches } = useAppSelector((state) => state.payment_batches)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View payment_batches')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View payment_batches')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/payment_batches/payment_batches-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{payment_batches?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>BatchNumber</p>
|
||||
<p>{payment_batches?.batch_number}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{payment_batches?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{payment_batches?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>TotalAmount</p>
|
||||
<p>{payment_batches?.total_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='ScheduledAt'>
|
||||
{payment_batches.scheduled_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={payment_batches.scheduled_at ?
|
||||
new Date(
|
||||
dayjs(payment_batches.scheduled_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No ScheduledAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='ProcessedAt'>
|
||||
{payment_batches.processed_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={payment_batches.processed_at ?
|
||||
new Date(
|
||||
dayjs(payment_batches.processed_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No ProcessedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>PreparedBy</p>
|
||||
|
||||
|
||||
<p>{payment_batches?.prepared_by_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Payments PaymentBatch</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>PaymentReference</th>
|
||||
|
||||
|
||||
|
||||
<th>PaymentDate</th>
|
||||
|
||||
|
||||
|
||||
<th>Amount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>FailureReason</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{payment_batches.payments_batch && Array.isArray(payment_batches.payments_batch) &&
|
||||
payment_batches.payments_batch.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/payments/payments-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="payment_reference">
|
||||
{ item.payment_reference }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="payment_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.payment_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="amount">
|
||||
{ item.amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="failure_reason">
|
||||
{ item.failure_reason }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!payment_batches?.payments_batch?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/payment_batches/payment_batches-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PaymentBatchesView;
|
||||
Payment_batchesView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_PAYMENT_BATCHES'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Payment_batchesView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -372,14 +371,28 @@ const EditPayment_requestsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Payment Request"
|
||||
pluralLabel="Payment Requests"
|
||||
listHref="/payment_requests/payment_requests-list"
|
||||
viewHref={`/payment_requests/payment_requests-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1073,7 +1086,6 @@ const EditPayment_requestsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/payment_requests/payment_requests-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
@ -1,22 +1,874 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { fetch } from '../../stores/payment_requests/payment_requestsSlice';
|
||||
import EntityRecordViewPage from '../../components/EntityPage/EntityRecordViewPage';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import dayjs from "dayjs";
|
||||
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
|
||||
import {useRouter} from "next/router";
|
||||
import { fetch } from '../../stores/payment_requests/payment_requestsSlice'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import {getPageTitle} from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import BaseDivider from "../../components/BaseDivider";
|
||||
import {mdiChartTimelineVariant} from "@mdi/js";
|
||||
import {SwitchField} from "../../components/SwitchField";
|
||||
import FormField from "../../components/FormField";
|
||||
|
||||
const PaymentRequestsView = () => (
|
||||
<EntityRecordViewPage
|
||||
singularLabel="Payment Request"
|
||||
pluralLabel="Payment Requests"
|
||||
stateKey="payment_requests"
|
||||
recordKey="payment_requests"
|
||||
fetchRecord={fetch}
|
||||
listHref="/payment_requests/payment_requests-list"
|
||||
editHref={(id) => `/payment_requests/payment_requests-edit/?id=${id ?? ''}`}
|
||||
/>
|
||||
);
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
PaymentRequestsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated permission={'READ_PAYMENT_REQUESTS'}>{page}</LayoutAuthenticated>;
|
||||
|
||||
const Payment_requestsView = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { payment_requests } = useAppSelector((state) => state.payment_requests)
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View payment_requests')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View payment_requests')} main>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/payment_requests/payment_requests-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Organization</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{payment_requests?.organization?.name ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Invoice</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>{payment_requests?.invoice?.invoice_number ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RequestNumber</p>
|
||||
<p>{payment_requests?.request_number}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RequestedAmount</p>
|
||||
<p>{payment_requests?.requested_amount || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Currency</p>
|
||||
<p>{payment_requests?.currency ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>PaymentMethod</p>
|
||||
<p>{payment_requests?.payment_method ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{payment_requests?.status ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>RequestedBy</p>
|
||||
|
||||
|
||||
<p>{payment_requests?.requested_by_user?.firstName ?? 'No data'}</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='RequestedAt'>
|
||||
{payment_requests.requested_at ? <DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
showTimeSelect
|
||||
selected={payment_requests.requested_at ?
|
||||
new Date(
|
||||
dayjs(payment_requests.requested_at).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
disabled
|
||||
/> : <p>No RequestedAt</p>}
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='Multi Text' hasTextareaHeight>
|
||||
<textarea className={'w-full'} disabled value={payment_requests?.justification} />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Payments PaymentRequest</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>PaymentReference</th>
|
||||
|
||||
|
||||
|
||||
<th>PaymentDate</th>
|
||||
|
||||
|
||||
|
||||
<th>Amount</th>
|
||||
|
||||
|
||||
|
||||
<th>Currency</th>
|
||||
|
||||
|
||||
|
||||
<th>Status</th>
|
||||
|
||||
|
||||
|
||||
<th>FailureReason</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{payment_requests.payments_payment_request && Array.isArray(payment_requests.payments_payment_request) &&
|
||||
payment_requests.payments_payment_request.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/payments/payments-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="payment_reference">
|
||||
{ item.payment_reference }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="payment_date">
|
||||
{ dataFormatter.dateTimeFormatter(item.payment_date) }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="amount">
|
||||
{ item.amount }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="currency">
|
||||
{ item.currency }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="status">
|
||||
{ item.status }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="failure_reason">
|
||||
{ item.failure_reason }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!payment_requests?.payments_payment_request?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/payment_requests/payment_requests-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PaymentRequestsView;
|
||||
Payment_requestsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'READ_PAYMENT_REQUESTS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default Payment_requestsView;
|
||||
@ -10,7 +10,6 @@ import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||
import SectionMain from '../../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||
import { getPageTitle } from '../../config'
|
||||
import EnhancedEntityEditShell from '../../components/EntityPage/EnhancedEntityEditShell'
|
||||
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import FormField from '../../components/FormField'
|
||||
@ -372,14 +371,28 @@ const EditPaymentsPage = () => {
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
<EnhancedEntityEditShell
|
||||
entityLabel="Payment"
|
||||
pluralLabel="Payments"
|
||||
listHref="/payments/payments-list"
|
||||
viewHref={`/payments/payments-view/?id=${id}`}
|
||||
record={initialValues}
|
||||
>
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') &&
|
||||
<FormField label='Organization' labelFor='organization'>
|
||||
<Field
|
||||
name='organization'
|
||||
@ -1165,7 +1178,6 @@ const EditPaymentsPage = () => {
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/payments/payments-list')}/>
|
||||
</BaseButtons>
|
||||
</EnhancedEntityEditShell>
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user