Compare commits

..

No commits in common. "ai-dev" and "master" have entirely different histories.

13 changed files with 135 additions and 486 deletions

5
.gitignore vendored
View File

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

File diff suppressed because one or more lines are too long

View File

@ -1,36 +0,0 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -19,11 +19,11 @@ const CustomersData = [
// type code here for "relation_one" field
PhoneNumber: 'Francis Galton',
PhoneNumber: 'Albert Einstein',
InvoicingEmail: 'Hans Bethe',
InvoicingEmail: 'Comte de Buffon',
Address: 'John Dalton',
Address: 'Antoine Laurent Lavoisier',
},
{
@ -33,11 +33,11 @@ const CustomersData = [
// type code here for "relation_one" field
PhoneNumber: 'Carl Linnaeus',
PhoneNumber: 'Arthur Eddington',
InvoicingEmail: 'Euclid',
InvoicingEmail: 'Emil Kraepelin',
Address: 'Hermann von Helmholtz',
Address: 'Sigmund Freud',
},
{
@ -47,39 +47,11 @@ const CustomersData = [
// type code here for "relation_one" field
PhoneNumber: 'Jonas Salk',
PhoneNumber: 'George Gaylord Simpson',
InvoicingEmail: 'Gertrude Belle Elion',
InvoicingEmail: 'Charles Lyell',
Address: 'Franz Boas',
},
{
name: 'Umbrella Corp',
// type code here for "relation_many" field
// type code here for "relation_one" field
PhoneNumber: 'Erwin Schrodinger',
InvoicingEmail: 'Max Delbruck',
Address: 'Karl Landsteiner',
},
{
name: 'Hooli',
// type code here for "relation_many" field
// type code here for "relation_one" field
PhoneNumber: 'Ludwig Boltzmann',
InvoicingEmail: 'Anton van Leeuwenhoek',
Address: 'Edward Teller',
Address: 'Hermann von Helmholtz',
},
];
@ -97,17 +69,17 @@ const ProjectsData = [
// type code here for "relation_one" field
DevelopmentCost: 28.78,
DevelopmentCost: 68.78,
MaintenanceCost: 84.45,
MaintenanceCost: 70.05,
ExpenseCost: 'Comte de Buffon',
ExpenseCost: 'B. F. Skinner',
Profits: 28.31,
Profits: 82.01,
StartDate: new Date(Date.now()),
EndDate: 'Lynn Margulis',
EndDate: 'Max Planck',
},
{
@ -123,17 +95,17 @@ const ProjectsData = [
// type code here for "relation_one" field
DevelopmentCost: 30.77,
DevelopmentCost: 68.69,
MaintenanceCost: 86.18,
MaintenanceCost: 90.16,
ExpenseCost: 'Marie Curie',
ExpenseCost: 'Albrecht von Haller',
Profits: 62.16,
Profits: 63.75,
StartDate: new Date(Date.now()),
EndDate: 'Pierre Simon de Laplace',
EndDate: 'Lynn Margulis',
},
{
@ -149,77 +121,25 @@ const ProjectsData = [
// type code here for "relation_one" field
DevelopmentCost: 64.96,
DevelopmentCost: 78.44,
MaintenanceCost: 79.67,
MaintenanceCost: 25.18,
ExpenseCost: 'Max Planck',
ExpenseCost: 'Jean Baptiste Lamarck',
Profits: 56.37,
StartDate: new Date(Date.now()),
EndDate: 'Lucretius',
},
{
name: 'Project Delta',
// type code here for "relation_one" field
// type code here for "relation_many" field
budget: 150000,
// type code here for "relation_one" field
// type code here for "relation_one" field
DevelopmentCost: 90.49,
MaintenanceCost: 32.89,
ExpenseCost: 'James Watson',
Profits: 20.72,
Profits: 51.98,
StartDate: new Date(Date.now()),
EndDate: 'Edwin Hubble',
},
{
name: 'Project Epsilon',
// type code here for "relation_one" field
// type code here for "relation_many" field
budget: 200000,
// type code here for "relation_one" field
// type code here for "relation_one" field
DevelopmentCost: 78.95,
MaintenanceCost: 88.86,
ExpenseCost: 'Ernst Haeckel',
Profits: 66.52,
StartDate: new Date(Date.now()),
EndDate: 'Charles Darwin',
},
];
const TasksData = [
{
title: 'Design Phase',
status: 'Completed',
status: 'InProgress',
// type code here for "relation_one" field
@ -231,25 +151,25 @@ const TasksData = [
TaskType: 'User Story',
EpicEstimatedCost: 69.74,
EpicEstimatedCost: 78.27,
EpicActualCost: 15.68,
EpicActualCost: 82.44,
EpicEstimatedTime: 2,
EpicEstimatedTime: 3,
EpicCostVariance: 48.99,
EpicCostVariance: 80.45,
EpicActualTime: 7,
EpicVarianceTime: 7,
EpicVarianceTime: 2,
EpicCompletionRate: 'Joseph J. Thomson',
EpicCompletionRate: 'William Harvey',
},
{
title: 'Development Phase',
status: 'Completed',
status: 'InProgress',
// type code here for "relation_one" field
@ -259,27 +179,27 @@ const TasksData = [
// type code here for "relation_one" field
TaskType: 'User Story',
TaskType: 'Feature',
EpicEstimatedCost: 72.24,
EpicEstimatedCost: 26.45,
EpicActualCost: 31.96,
EpicActualCost: 82.28,
EpicEstimatedTime: 5,
EpicEstimatedTime: 8,
EpicCostVariance: 31.11,
EpicCostVariance: 95.76,
EpicActualTime: 9,
EpicActualTime: 4,
EpicVarianceTime: 7,
EpicVarianceTime: 1,
EpicCompletionRate: 'Wilhelm Wundt',
EpicCompletionRate: 'Sigmund Freud',
},
{
title: 'Testing Phase',
status: 'Completed',
status: 'NotStarted',
// type code here for "relation_one" field
@ -289,81 +209,21 @@ const TasksData = [
// type code here for "relation_one" field
TaskType: 'User Story',
TaskType: 'Feature',
EpicEstimatedCost: 52.63,
EpicEstimatedCost: 24.21,
EpicActualCost: 70.55,
EpicActualCost: 92.46,
EpicEstimatedTime: 7,
EpicCostVariance: 11.14,
EpicCostVariance: 55.98,
EpicActualTime: 7,
EpicActualTime: 9,
EpicVarianceTime: 5,
EpicVarianceTime: 3,
EpicCompletionRate: 'Johannes Kepler',
},
{
title: 'Deployment Phase',
status: 'Completed',
// type code here for "relation_one" field
start_date: new Date('2024-01-01T09:00:00Z'),
end_date: new Date('2024-01-15T17:00:00Z'),
// type code here for "relation_one" field
TaskType: 'Epic',
EpicEstimatedCost: 76.17,
EpicActualCost: 17.91,
EpicEstimatedTime: 5,
EpicCostVariance: 78.78,
EpicActualTime: 6,
EpicVarianceTime: 9,
EpicCompletionRate: 'John Bardeen',
},
{
title: 'Requirement Gathering',
status: 'Completed',
// type code here for "relation_one" field
start_date: new Date('2023-10-01T09:00:00Z'),
end_date: new Date('2023-10-15T17:00:00Z'),
// type code here for "relation_one" field
TaskType: 'Epic',
EpicEstimatedCost: 62.61,
EpicActualCost: 79.85,
EpicEstimatedTime: 3,
EpicCostVariance: 33.23,
EpicActualTime: 1,
EpicVarianceTime: 8,
EpicCompletionRate: 'Alfred Kinsey',
EpicCompletionRate: 'Alfred Wegener',
},
];
@ -403,30 +263,6 @@ const TimesheetsData = [
// type code here for "relation_one" field
},
{
// type code here for "relation_one" field
// type code here for "relation_one" field
hours: 5,
date: new Date('2023-11-04T09:00:00Z'),
// type code here for "relation_one" field
},
{
// type code here for "relation_one" field
// type code here for "relation_one" field
hours: 9,
date: new Date('2023-11-05T09:00:00Z'),
// type code here for "relation_one" field
},
];
const OrganizationsData = [
@ -441,14 +277,6 @@ const OrganizationsData = [
{
name: 'Blue Sky Enterprises',
},
{
name: 'Future Tech',
},
{
name: 'Creative Minds',
},
];
// Similar logic for "relation_many"
@ -486,28 +314,6 @@ async function associateUserWithOrganization() {
if (User2?.setOrganization) {
await User2.setOrganization(relatedOrganization2);
}
const relatedOrganization3 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const User3 = await Users.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (User3?.setOrganization) {
await User3.setOrganization(relatedOrganization3);
}
const relatedOrganization4 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const User4 = await Users.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (User4?.setOrganization) {
await User4.setOrganization(relatedOrganization4);
}
}
// Similar logic for "relation_many"
@ -545,28 +351,6 @@ async function associateCustomerWithOrganization() {
if (Customer2?.setOrganization) {
await Customer2.setOrganization(relatedOrganization2);
}
const relatedOrganization3 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Customer3 = await Customers.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Customer3?.setOrganization) {
await Customer3.setOrganization(relatedOrganization3);
}
const relatedOrganization4 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Customer4 = await Customers.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Customer4?.setOrganization) {
await Customer4.setOrganization(relatedOrganization4);
}
}
async function associateProjectWithCustomer() {
@ -602,28 +386,6 @@ async function associateProjectWithCustomer() {
if (Project2?.setCustomer) {
await Project2.setCustomer(relatedCustomer2);
}
const relatedCustomer3 = await Customers.findOne({
offset: Math.floor(Math.random() * (await Customers.count())),
});
const Project3 = await Projects.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Project3?.setCustomer) {
await Project3.setCustomer(relatedCustomer3);
}
const relatedCustomer4 = await Customers.findOne({
offset: Math.floor(Math.random() * (await Customers.count())),
});
const Project4 = await Projects.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Project4?.setCustomer) {
await Project4.setCustomer(relatedCustomer4);
}
}
// Similar logic for "relation_many"
@ -661,28 +423,6 @@ async function associateProjectWithOrganization() {
if (Project2?.setOrganization) {
await Project2.setOrganization(relatedOrganization2);
}
const relatedOrganization3 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Project3 = await Projects.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Project3?.setOrganization) {
await Project3.setOrganization(relatedOrganization3);
}
const relatedOrganization4 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Project4 = await Projects.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Project4?.setOrganization) {
await Project4.setOrganization(relatedOrganization4);
}
}
async function associateProjectWithOrganization() {
@ -718,28 +458,6 @@ async function associateProjectWithOrganization() {
if (Project2?.setOrganization) {
await Project2.setOrganization(relatedOrganization2);
}
const relatedOrganization3 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Project3 = await Projects.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Project3?.setOrganization) {
await Project3.setOrganization(relatedOrganization3);
}
const relatedOrganization4 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Project4 = await Projects.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Project4?.setOrganization) {
await Project4.setOrganization(relatedOrganization4);
}
}
async function associateTaskWithProject() {
@ -775,28 +493,6 @@ async function associateTaskWithProject() {
if (Task2?.setProject) {
await Task2.setProject(relatedProject2);
}
const relatedProject3 = await Projects.findOne({
offset: Math.floor(Math.random() * (await Projects.count())),
});
const Task3 = await Tasks.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Task3?.setProject) {
await Task3.setProject(relatedProject3);
}
const relatedProject4 = await Projects.findOne({
offset: Math.floor(Math.random() * (await Projects.count())),
});
const Task4 = await Tasks.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Task4?.setProject) {
await Task4.setProject(relatedProject4);
}
}
async function associateTaskWithOrganization() {
@ -832,28 +528,6 @@ async function associateTaskWithOrganization() {
if (Task2?.setOrganization) {
await Task2.setOrganization(relatedOrganization2);
}
const relatedOrganization3 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Task3 = await Tasks.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Task3?.setOrganization) {
await Task3.setOrganization(relatedOrganization3);
}
const relatedOrganization4 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Task4 = await Tasks.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Task4?.setOrganization) {
await Task4.setOrganization(relatedOrganization4);
}
}
async function associateTimesheetWithUser() {
@ -889,28 +563,6 @@ async function associateTimesheetWithUser() {
if (Timesheet2?.setUser) {
await Timesheet2.setUser(relatedUser2);
}
const relatedUser3 = await Users.findOne({
offset: Math.floor(Math.random() * (await Users.count())),
});
const Timesheet3 = await Timesheets.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Timesheet3?.setUser) {
await Timesheet3.setUser(relatedUser3);
}
const relatedUser4 = await Users.findOne({
offset: Math.floor(Math.random() * (await Users.count())),
});
const Timesheet4 = await Timesheets.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Timesheet4?.setUser) {
await Timesheet4.setUser(relatedUser4);
}
}
async function associateTimesheetWithTask() {
@ -946,28 +598,6 @@ async function associateTimesheetWithTask() {
if (Timesheet2?.setTask) {
await Timesheet2.setTask(relatedTask2);
}
const relatedTask3 = await Tasks.findOne({
offset: Math.floor(Math.random() * (await Tasks.count())),
});
const Timesheet3 = await Timesheets.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Timesheet3?.setTask) {
await Timesheet3.setTask(relatedTask3);
}
const relatedTask4 = await Tasks.findOne({
offset: Math.floor(Math.random() * (await Tasks.count())),
});
const Timesheet4 = await Timesheets.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Timesheet4?.setTask) {
await Timesheet4.setTask(relatedTask4);
}
}
async function associateTimesheetWithOrganization() {
@ -1003,28 +633,6 @@ async function associateTimesheetWithOrganization() {
if (Timesheet2?.setOrganization) {
await Timesheet2.setOrganization(relatedOrganization2);
}
const relatedOrganization3 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Timesheet3 = await Timesheets.findOne({
order: [['id', 'ASC']],
offset: 3,
});
if (Timesheet3?.setOrganization) {
await Timesheet3.setOrganization(relatedOrganization3);
}
const relatedOrganization4 = await Organizations.findOne({
offset: Math.floor(Math.random() * (await Organizations.count())),
});
const Timesheet4 = await Timesheets.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Timesheet4?.setOrganization) {
await Timesheet4.setOrganization(relatedOrganization4);
}
}
module.exports = {

View File

@ -31,6 +31,9 @@ router.use(checkCrudPermissions('customers'));
* InvoicingEmail:
* type: string
* default: InvoicingEmail
* Address:
* type: string
* default: Address
*/
@ -316,7 +319,7 @@ router.get(
currentUser,
});
if (filetype && filetype === 'csv') {
const fields = ['id', 'name', 'PhoneNumber', 'InvoicingEmail'];
const fields = ['id', 'name', 'PhoneNumber', 'InvoicingEmail', 'Address'];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);

View File

@ -43,7 +43,7 @@ module.exports = class SearchService {
const tableColumns = {
users: ['firstName', 'lastName', 'phoneNumber', 'email'],
customers: ['name', 'PhoneNumber', 'InvoicingEmail'],
customers: ['name', 'PhoneNumber', 'InvoicingEmail', 'Address'],
projects: ['name', 'ExpenseCost', 'EndDate'],

View File

@ -1 +0,0 @@
{}

View File

@ -83,6 +83,19 @@ const CardCustomers = ({
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Projects
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{dataFormatter
.projectsManyListFormatter(item.projects)
.join(', ')}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Phone Number
@ -104,6 +117,17 @@ const CardCustomers = ({
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Address
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.Address}
</div>
</dd>
</div>
</dl>
</li>
))}

View File

@ -56,6 +56,15 @@ const ListCustomers = ({
<p className={'line-clamp-2'}>{item.name}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Projects</p>
<p className={'line-clamp-2'}>
{dataFormatter
.projectsManyListFormatter(item.projects)
.join(', ')}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Phone Number</p>
<p className={'line-clamp-2'}>{item.PhoneNumber}</p>
@ -67,6 +76,11 @@ const ListCustomers = ({
</p>
<p className={'line-clamp-2'}>{item.InvoicingEmail}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Address</p>
<p className={'line-clamp-2'}>{item.Address}</p>
</div>
</Link>
<ListActionsPopover
onDelete={onDelete}

View File

@ -50,6 +50,25 @@ export const loadColumns = async (
editable: hasUpdatePermission,
},
{
field: 'projects',
headerName: 'Projects',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: false,
sortable: false,
type: 'singleSelect',
valueFormatter: ({ value }) =>
dataFormatter.projectsManyListFormatter(value).join(', '),
renderEditCell: (params) => (
<DataGridMultiSelect {...params} entityName={'projects'} />
),
},
{
field: 'PhoneNumber',
headerName: 'Phone Number',
@ -74,6 +93,18 @@ export const loadColumns = async (
editable: hasUpdatePermission,
},
{
field: 'Address',
headerName: 'Address',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'actions',
type: 'actions',

View File

@ -32,6 +32,9 @@ const CustomersTablesPage = () => {
{ label: 'Name', title: 'name' },
{ label: 'Phone Number', title: 'PhoneNumber' },
{ label: 'Invoicing Email', title: 'InvoicingEmail' },
{ label: 'Address', title: 'Address' },
{ label: 'Projects', title: 'projects' },
]);
const hasCreatePermission =

View File

@ -32,6 +32,9 @@ const CustomersTablesPage = () => {
{ label: 'Name', title: 'name' },
{ label: 'Phone Number', title: 'PhoneNumber' },
{ label: 'Invoicing Email', title: 'InvoicingEmail' },
{ label: 'Address', title: 'Address' },
{ label: 'Projects', title: 'projects' },
]);
const hasCreatePermission =

View File

@ -131,6 +131,8 @@ const OrganizationsView = () => {
<th>Phone Number</th>
<th>Invoicing Email</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@ -152,6 +154,8 @@ const OrganizationsView = () => {
<td data-label='InvoicingEmail'>
{item.InvoicingEmail}
</td>
<td data-label='Address'>{item.Address}</td>
</tr>
))}
</tbody>