Skip to content
3 changes: 3 additions & 0 deletions src/models/page.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export interface Page extends Document {
record: mongoose.Types.ObjectId | Record;
}
) & {
geographic?: string;
} & {
content: mongoose.Types.ObjectId | Form | Workflow | Dashboard;
})[];
geographicContext: PageGeographicContextT;
Expand Down Expand Up @@ -88,6 +90,7 @@ const pageSchema = new Schema<Page>(
contentWithContext: [
{
element: mongoose.Schema.Types.Mixed,
geographic: mongoose.Schema.Types.Mixed,
record: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Record',
Expand Down
118 changes: 87 additions & 31 deletions src/schema/mutation/addDashboardWithContext.mutation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { GraphQLNonNull, GraphQLError, GraphQLID } from 'graphql';
import {
GraphQLNonNull,
GraphQLError,
GraphQLID,
GraphQLString,
} from 'graphql';
import {
ApiConfiguration,
Dashboard,
Expand All @@ -24,16 +29,18 @@ import { get, isNil } from 'lodash';
* @param dashboard The dashboard being duplicated
* @param context The context of the dashboard
* @param id The id of the record or element
* @param geographicContext Geographic context value
* @param dataSources The data sources
* @returns The name of the new dashboard
*/
const getNewDashboardName = async (
dashboard: Dashboard,
context: Page['context'],
id: string | Types.ObjectId,
geographicContext: string,
dataSources: any
) => {
if ('refData' in context && context.refData) {
if ('refData' in context && context.refData && id) {
// Get items from reference data
const referenceData = await ReferenceData.findById(context.refData);
const apiConfiguration = await ApiConfiguration.findById(
Expand All @@ -46,14 +53,22 @@ const getNewDashboardName = async (
: referenceData.data;

const item = data.find((x) => x[referenceData.valueField] === id);
if (geographicContext) {
return `${item?.[context.displayField]} - ${geographicContext}`;
}
return `${item?.[context.displayField]}`;
} else if ('resource' in context && context.resource) {
} else if ('resource' in context && context.resource && id) {
const record = await Record.findById(id);
if (geographicContext) {
return `${record.data[context.displayField]} - ${geographicContext}`;
}
return `${record.data[context.displayField]}`;
}

// Default return, should never happen
return dashboard.name;
// Default return
if (geographicContext) {
return `${dashboard.name} - ${geographicContext}`;
}
return `${dashboard.name}`;
};

/**
Expand All @@ -64,6 +79,7 @@ const getNewDashboardName = async (
* @param entry new entry
* @param entry.element new element ( if ref data )
* @param entry.record new record ( if resource )
* @param entry.geographic new geographic
* @returns is entry duplicated or not
*/
const hasDuplicate = (
Expand All @@ -72,26 +88,51 @@ const hasDuplicate = (
entry: {
element?: any;
record?: string | Types.ObjectId;
geographic?: string;
}
) => {
const uniqueEntries = new Set();
if (!isNil(get(context, 'resource'))) {
for (const item of contentWithContext) {
if (get(item, 'record')) {
uniqueEntries.add((item as any).record.toString());

if ('geographic' in entry && 'geography') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @RenzoPrats , this is the only thing that I don't get, why set && 'geography' in the condition, that is always true right? Is it necessary?

Besides that looks good to me 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @unai-reliefapp, indeed it was useless I think I misspelled it, it's already fixed, thank you!

const contextTypeKey =
'record' in entry ? 'record' : 'element' in entry ? 'element' : null;
// record and geographic
if (contextTypeKey) {
const contains = contentWithContext.some(
(item: any) =>
item[contextTypeKey] === entry[contextTypeKey] &&
item.geographic === entry.geographic
);
if (contains) {
return true;
}
// geographic
} else {
for (const item of contentWithContext) {
if (get(item, 'geographic')) {
uniqueEntries.add((item as any).geographic.toString());
}
}
if (uniqueEntries.has(entry.geographic.toString())) {
return true;
}
}
if (uniqueEntries.has(entry.record.toString())) {
return true;
}
// record or element
} else {
for (const item of contentWithContext) {
if (get(item, 'element')) {
uniqueEntries.add((item as any).element.toString());
const contextTypeKey = !isNil(get(context, 'resource'))
? 'record'
: !isNil(get(context, 'element'))
? 'element'
: null;
if (contextTypeKey) {
for (const item of contentWithContext) {
if (get(item, contextTypeKey)) {
uniqueEntries.add((item as any)[contextTypeKey].toString());
}
}
if (uniqueEntries.has(entry[contextTypeKey].toString())) {
return true;
}
}
if (uniqueEntries.has(entry.element.toString())) {
return true;
}
}
return false;
Expand All @@ -102,6 +143,7 @@ type AddDashboardWithContextArgs = {
page: string;
element?: any;
record?: string | Types.ObjectId;
geographic?: string;
};

/**
Expand All @@ -114,14 +156,18 @@ export default {
page: { type: new GraphQLNonNull(GraphQLID) },
element: { type: GraphQLJSON },
record: { type: GraphQLID },
geographic: { type: GraphQLString },
},
async resolve(parent, args: AddDashboardWithContextArgs, context: Context) {
// Authentication check
graphQLAuthCheck(context);
try {
const user = context.user;
// Check arguments
if ((!args.element && !args.record) || (args.element && args.record)) {
if (
(!args.element && !args.record && !args.geographic) ||
(args.element && args.record)
) {
throw new GraphQLError(
context.i18next.t(
'mutations.dashboard.addWithContext.errors.invalidArguments'
Expand Down Expand Up @@ -167,13 +213,13 @@ export default {
hasDuplicate(page.context, page.contentWithContext, {
...(args.record && { record: args.record }),
...(args.element && { element: args.element }),
...(args.geographic && { geographic: args.geographic }),
})
) {
throw new GraphQLError(
context.i18next.t('mutations.dashboard.add.errors.invalidPageType')
);
}

// Fetches the dashboard from the page
const template = await Dashboard.findById(page.content);
// Duplicates the dashboard
Expand All @@ -182,26 +228,36 @@ export default {
template,
page.context,
args.record || args.element,
args.geographic,
context.dataSources
),
// Copy structure from template dashboard
structure: template.structure || [],
}).save();

const newContentWithContext = args.record
? ({
record: args.record,
let newContentWithContext: any;
const contextKey = args.record
? 'record'
: args.element
? 'element'
: null;
if (!args.geographic) {
if (contextKey) {
newContentWithContext = {
[contextKey]: args[contextKey],
content: newDashboard._id,
} as Page['contentWithContext'][number])
: ({
element: args.element,
content: newDashboard._id,
} as Page['contentWithContext'][number]);

} as Page['contentWithContext'][number];
}
} else {
newContentWithContext = {
...(contextKey && { [contextKey]: args[contextKey] }),
geographic: args.geographic,
content: newDashboard._id,
} as Page['contentWithContext'][number];
}
// Adds the dashboard to the page
page.contentWithContext.push(newContentWithContext);
await page.save();

return newDashboard;
} catch (err) {
logger.error(err.message, { stack: err.stack });
Expand Down
2 changes: 0 additions & 2 deletions src/schema/mutation/editPage.mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,8 @@ export default {

// Update geographic context
if (!isNil(args.geographicContext)) {
const geographicContext = page.geographicContext;
Object.assign(update, {
geographicContext: {
...geographicContext,
...args.geographicContext,
},
});
Expand Down