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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions backend/actions/Model/addField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

const Archetype = require('archetype');
const authorize = require('../../authorize');

const AddFieldParams = new Archetype({
model: {
$type: 'string',
$required: true
},
_id: {
$type: Archetype.Any,
$required: true
},
fieldName: {
$type: 'string',
$required: true
},
fieldValue: {
$type: Archetype.Any,
$required: true
},
roles: {
$type: ['string']
}
}).compile('AddFieldParams');

module.exports = ({ db }) => async function addField(params) {
const { model, _id, fieldName, fieldValue, roles } = new AddFieldParams(params);

await authorize('Model.updateDocument', roles);

const Model = db.models[model];
if (Model == null) {
throw new Error(`Model ${model} not found`);
}

// Create update object with the new field
const update = { $set: { [fieldName]: fieldValue } };

const doc = await Model.findByIdAndUpdate(
_id,
update,
{
sanitizeFilter: true,
returnDocument: 'after',
overwriteImmutable: true,
runValidators: false,
strict: false
}
);

return { doc };
};
4 changes: 3 additions & 1 deletion backend/actions/Model/getDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@ module.exports = ({ db }) => async function getDocument(params) {
}
removeSpecifiedPaths(schemaPaths, '.$*');

return { doc: doc.toJSON({ virtuals: true, getters: false, transform: false }), schemaPaths };
const virtualPaths = Object.keys(Model.schema.virtuals);

return { doc: doc.toJSON({ virtuals: true, getters: false, transform: false }), schemaPaths, virtualPaths };
};
1 change: 1 addition & 0 deletions backend/actions/Model/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

exports.addField = require('./addField');
exports.createDocument = require('./createDocument');
exports.deleteDocument = require('./deleteDocument');
exports.deleteDocuments = require('./deleteDocuments');
Expand Down
29 changes: 23 additions & 6 deletions backend/actions/Model/updateDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,32 @@
throw new Error(`Model ${model} not found`);
}

let processedUpdate = update;
let setFields = {};

Check warning on line 34 in backend/actions/Model/updateDocument.js

View workflow job for this annotation

GitHub Actions / lint

'setFields' is never reassigned. Use 'const' instead

Check warning on line 34 in backend/actions/Model/updateDocument.js

View workflow job for this annotation

GitHub Actions / lint

'setFields' is never reassigned. Use 'const' instead
let unsetFields = {};

Check warning on line 35 in backend/actions/Model/updateDocument.js

View workflow job for this annotation

GitHub Actions / lint

'unsetFields' is never reassigned. Use 'const' instead

Check warning on line 35 in backend/actions/Model/updateDocument.js

View workflow job for this annotation

GitHub Actions / lint

'unsetFields' is never reassigned. Use 'const' instead

if (Object.keys(update).length > 0) {
processedUpdate = Object.fromEntries(
Object.entries(update).map(([key, value]) => [key, value === 'null' ? null : value === 'undefined' ? undefined : value])
);
Object.entries(update).forEach(([key, value]) => {
if (value === 'null') {
setFields[key] = null;
} else if (value === 'undefined') {
// Use $unset to remove the field for undefined values
unsetFields[key] = "";

Check warning on line 43 in backend/actions/Model/updateDocument.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote

Check warning on line 43 in backend/actions/Model/updateDocument.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote
} else {
setFields[key] = value;
}
});
}

const doc = await Model.
findByIdAndUpdate(_id, processedUpdate, { sanitizeFilter: true, returnDocument: 'after', overwriteImmutable: true, runValidators: false });
// Build the update operation with both $set and $unset
const updateOperation = {};
if (Object.keys(setFields).length > 0) {
updateOperation.$set = setFields;
}
if (Object.keys(unsetFields).length > 0) {
updateOperation.$unset = unsetFields;
}

const doc = await Model.
findByIdAndUpdate(_id, updateOperation, { sanitizeFilter: true, returnDocument: 'after', overwriteImmutable: true, runValidators: false });
return { doc };
};
28 changes: 23 additions & 5 deletions backend/actions/Model/updateDocuments.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,33 @@
throw new Error(`Model ${model} not found`);
}

let processedUpdate = update;
let setFields = {};

Check warning on line 34 in backend/actions/Model/updateDocuments.js

View workflow job for this annotation

GitHub Actions / lint

'setFields' is never reassigned. Use 'const' instead

Check warning on line 34 in backend/actions/Model/updateDocuments.js

View workflow job for this annotation

GitHub Actions / lint

'setFields' is never reassigned. Use 'const' instead
let unsetFields = {};

Check warning on line 35 in backend/actions/Model/updateDocuments.js

View workflow job for this annotation

GitHub Actions / lint

'unsetFields' is never reassigned. Use 'const' instead

Check warning on line 35 in backend/actions/Model/updateDocuments.js

View workflow job for this annotation

GitHub Actions / lint

'unsetFields' is never reassigned. Use 'const' instead

if (Object.keys(update).length > 0) {
processedUpdate = Object.fromEntries(
Object.entries(update).map(([key, value]) => [key, value === 'null' ? null : value === 'undefined' ? undefined : value])
);
Object.entries(update).forEach(([key, value]) => {
if (value === 'null') {
setFields[key] = null;
} else if (value === 'undefined') {
// Use $unset to remove the field for undefined values
unsetFields[key] = "";

Check warning on line 43 in backend/actions/Model/updateDocuments.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote

Check warning on line 43 in backend/actions/Model/updateDocuments.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote
} else {
setFields[key] = value;
}
});
}

// Build the update operation with both $set and $unset
const updateOperation = {};
if (Object.keys(setFields).length > 0) {
updateOperation.$set = setFields;
}
if (Object.keys(unsetFields).length > 0) {
updateOperation.$unset = unsetFields;
}

const result = await Model.
updateMany({ _id: { $in: _id } }, processedUpdate, { overwriteImmutable: true, runValidators: false });
updateMany({ _id: { $in: _id } }, updateOperation, { overwriteImmutable: true, runValidators: false });

return { result };
};
6 changes: 6 additions & 0 deletions frontend/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
}
};
exports.Model = {
addField(params) {
return client.post('', { action: 'Model.addField', ...params }).then(res => res.data);
},
createChart(params) {
return client.post('', { action: 'Model.createChart', ...params }).then(res => res.data);
},
Expand Down Expand Up @@ -182,6 +185,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
}
};
exports.Model = {
addField(params) {
return client.post('/Model/addField', params).then(res => res.data);
},
createChart: function(params) {
return client.post('/Model/createChart', params).then(res => res.data);
},
Expand Down
98 changes: 98 additions & 0 deletions frontend/src/document-details/document-details.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,102 @@
.document-details .date-position {
float: right;
margin-top: -7px;
}

/* Add Field Modal Styles */
.add-field-modal {
max-width: 500px;
width: 100%;
}

.add-field-modal .modal-exit {
position: absolute;
top: 15px;
right: 20px;
font-size: 24px;
cursor: pointer;
color: #6b7280;
z-index: 10;
}

.add-field-modal .modal-exit:hover {
color: #374151;
}

.add-field-modal form {
max-height: 70vh;
overflow-y: auto;
}

.add-field-modal input[type="text"],
.add-field-modal input[type="email"],
.add-field-modal input[type="password"],
.add-field-modal select,
.add-field-modal textarea {
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

.add-field-modal input:focus,
.add-field-modal select:focus,
.add-field-modal textarea:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.add-field-modal .border-red-500 {
border-color: #ef4444 !important;
}

.add-field-modal .border-red-500:focus {
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1) !important;
}

/* CodeMirror styling in modal */
.add-field-modal .CodeMirror {
border: 1px solid #d1d5db;
border-radius: 0.375rem;
font-size: 14px;
height: auto;
min-height: 100px;
}

.add-field-modal .CodeMirror:focus-within {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.add-field-modal .CodeMirror.CodeMirror-focused {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

/* JSON View Styles */
.json-view {
width: 100%;
}

.json-view pre {
margin: 0;
max-height: 70vh;
overflow: auto;
line-height: 1.5;
}

.json-view pre::-webkit-scrollbar {
width: 8px;
height: 8px;
}

.json-view pre::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}

.json-view pre::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
}

.json-view pre::-webkit-scrollbar-thumb:hover {
background: #555;
}
Loading