@mongoosejs/studio 0.0.127 → 0.0.129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/backend/actions/Model/addField.js +54 -0
- package/backend/actions/Model/exportQueryResults.js +0 -1
- package/backend/actions/Model/getDocument.js +3 -1
- package/backend/actions/Model/getDocuments.js +14 -5
- package/backend/actions/Model/getDocumentsStream.js +14 -5
- package/backend/actions/Model/index.js +1 -0
- package/backend/actions/Model/updateDocument.js +23 -6
- package/backend/actions/Model/updateDocuments.js +23 -5
- package/frontend/public/app.js +613 -45
- package/frontend/public/tw.css +219 -2
- package/frontend/src/api.js +6 -0
- package/frontend/src/document/document.css +8 -0
- package/frontend/src/document/document.html +102 -8
- package/frontend/src/document/document.js +34 -1
- package/frontend/src/document-details/document-details.css +98 -0
- package/frontend/src/document-details/document-details.html +231 -19
- package/frontend/src/document-details/document-details.js +328 -3
- package/frontend/src/document-details/document-property/document-property.css +15 -0
- package/frontend/src/document-details/document-property/document-property.html +75 -31
- package/frontend/src/document-details/document-property/document-property.js +43 -2
- package/frontend/src/edit-boolean/edit-boolean.html +47 -0
- package/frontend/src/edit-boolean/edit-boolean.js +38 -0
- package/frontend/src/models/models.js +79 -30
- package/frontend/src/mothership.js +4 -0
- package/frontend/src/navbar/navbar.html +1 -1
- package/frontend/src/team/team.html +63 -4
- package/frontend/src/team/team.js +47 -1
- package/package.json +1 -1
package/frontend/public/app.js
CHANGED
|
@@ -79,6 +79,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
exports.Model = {
|
|
82
|
+
addField(params) {
|
|
83
|
+
return client.post('', { action: 'Model.addField', ...params }).then(res => res.data);
|
|
84
|
+
},
|
|
82
85
|
createChart(params) {
|
|
83
86
|
return client.post('', { action: 'Model.createChart', ...params }).then(res => res.data);
|
|
84
87
|
},
|
|
@@ -192,6 +195,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
192
195
|
}
|
|
193
196
|
};
|
|
194
197
|
exports.Model = {
|
|
198
|
+
addField(params) {
|
|
199
|
+
return client.post('/Model/addField', params).then(res => res.data);
|
|
200
|
+
},
|
|
195
201
|
createChart: function(params) {
|
|
196
202
|
return client.post('/Model/createChart', params).then(res => res.data);
|
|
197
203
|
},
|
|
@@ -1527,7 +1533,54 @@ appendCSS(__webpack_require__(/*! ./document-details.css */ "./frontend/src/docu
|
|
|
1527
1533
|
|
|
1528
1534
|
module.exports = app => app.component('document-details', {
|
|
1529
1535
|
template,
|
|
1530
|
-
props: ['document', 'schemaPaths', 'editting', 'changes', 'invalid'],
|
|
1536
|
+
props: ['document', 'schemaPaths', 'virtualPaths', 'editting', 'changes', 'invalid', 'viewMode'],
|
|
1537
|
+
data() {
|
|
1538
|
+
return {
|
|
1539
|
+
searchQuery: '',
|
|
1540
|
+
selectedType: '',
|
|
1541
|
+
collapsedVirtuals: new Set(),
|
|
1542
|
+
showAddFieldModal: false,
|
|
1543
|
+
fieldData: {
|
|
1544
|
+
name: '',
|
|
1545
|
+
type: '',
|
|
1546
|
+
value: ''
|
|
1547
|
+
},
|
|
1548
|
+
fieldErrors: {},
|
|
1549
|
+
isSubmittingField: false,
|
|
1550
|
+
fieldValueEditor: null
|
|
1551
|
+
};
|
|
1552
|
+
},
|
|
1553
|
+
mounted() {
|
|
1554
|
+
// Focus on search input when component loads
|
|
1555
|
+
this.$nextTick(() => {
|
|
1556
|
+
if (this.$refs.searchInput) {
|
|
1557
|
+
this.$refs.searchInput.focus();
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
if (this.showAddFieldModal) {
|
|
1561
|
+
this.initializeFieldValueEditor();
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
},
|
|
1565
|
+
beforeDestroy() {
|
|
1566
|
+
this.destroyFieldValueEditor();
|
|
1567
|
+
},
|
|
1568
|
+
watch: {
|
|
1569
|
+
'fieldData.type'(newType, oldType) {
|
|
1570
|
+
// When field type changes, we need to handle the transition
|
|
1571
|
+
if (newType !== oldType) {
|
|
1572
|
+
// Destroy existing CodeMirror if it exists
|
|
1573
|
+
this.destroyFieldValueEditor();
|
|
1574
|
+
|
|
1575
|
+
// If switching to a type that needs CodeMirror, initialize it
|
|
1576
|
+
if (this.shouldUseCodeMirror) {
|
|
1577
|
+
this.$nextTick(() => {
|
|
1578
|
+
this.initializeFieldValueEditor();
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
},
|
|
1531
1584
|
computed: {
|
|
1532
1585
|
virtuals() {
|
|
1533
1586
|
if (this.schemaPaths == null) {
|
|
@@ -1541,15 +1594,294 @@ module.exports = app => app.component('document-details', {
|
|
|
1541
1594
|
const result = [];
|
|
1542
1595
|
for (let i = 0; i < docKeys.length; i++) {
|
|
1543
1596
|
if (!exists.includes(docKeys[i])) {
|
|
1544
|
-
|
|
1597
|
+
const isVirtual = this.virtualPaths && this.virtualPaths.includes(docKeys[i]);
|
|
1598
|
+
result.push({
|
|
1599
|
+
name: docKeys[i],
|
|
1600
|
+
value: this.document[docKeys[i]],
|
|
1601
|
+
isVirtual: isVirtual,
|
|
1602
|
+
isUserAdded: !isVirtual
|
|
1603
|
+
});
|
|
1545
1604
|
}
|
|
1546
1605
|
}
|
|
1547
1606
|
|
|
1548
1607
|
return result;
|
|
1608
|
+
},
|
|
1609
|
+
availableTypes() {
|
|
1610
|
+
if (!this.schemaPaths) return [];
|
|
1611
|
+
const types = new Set();
|
|
1612
|
+
this.schemaPaths.forEach(path => {
|
|
1613
|
+
if (path.instance) {
|
|
1614
|
+
types.add(path.instance);
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
// Add virtual field types to the available types
|
|
1619
|
+
this.virtuals.forEach(virtual => {
|
|
1620
|
+
const virtualType = this.getVirtualFieldType(virtual);
|
|
1621
|
+
if (virtualType && virtualType !== 'unknown') {
|
|
1622
|
+
types.add(virtualType);
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
return Array.from(types).sort();
|
|
1627
|
+
},
|
|
1628
|
+
allFieldTypes() {
|
|
1629
|
+
const schemaTypes = this.availableTypes;
|
|
1630
|
+
const commonTypes = ['String', 'Number', 'Boolean', 'Date', 'Array', 'Object'];
|
|
1631
|
+
|
|
1632
|
+
// Combine schema types with common types, avoiding duplicates
|
|
1633
|
+
const allTypes = new Set([...schemaTypes, ...commonTypes]);
|
|
1634
|
+
return Array.from(allTypes).sort();
|
|
1635
|
+
},
|
|
1636
|
+
shouldUseCodeMirror() {
|
|
1637
|
+
return ['Array', 'Object', 'Embedded'].includes(this.fieldData.type);
|
|
1638
|
+
},
|
|
1639
|
+
shouldUseDatePicker() {
|
|
1640
|
+
return this.fieldData.type === 'Date';
|
|
1641
|
+
},
|
|
1642
|
+
filteredSchemaPaths() {
|
|
1643
|
+
let paths = this.schemaPaths || [];
|
|
1644
|
+
|
|
1645
|
+
// Filter by search query
|
|
1646
|
+
if (this.searchQuery.trim()) {
|
|
1647
|
+
const query = this.searchQuery.toLowerCase();
|
|
1648
|
+
paths = paths.filter(path =>
|
|
1649
|
+
path.path.toLowerCase().includes(query)
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// Filter by data type
|
|
1654
|
+
if (this.selectedType) {
|
|
1655
|
+
paths = paths.filter(path =>
|
|
1656
|
+
path.instance === this.selectedType
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
return paths;
|
|
1661
|
+
},
|
|
1662
|
+
filteredVirtuals() {
|
|
1663
|
+
let virtuals = this.virtuals;
|
|
1664
|
+
|
|
1665
|
+
// Filter by search query
|
|
1666
|
+
if (this.searchQuery.trim()) {
|
|
1667
|
+
const query = this.searchQuery.toLowerCase();
|
|
1668
|
+
virtuals = virtuals.filter(virtual =>
|
|
1669
|
+
virtual.name.toLowerCase().includes(query)
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
// Filter by data type
|
|
1674
|
+
if (this.selectedType) {
|
|
1675
|
+
virtuals = virtuals.filter(virtual => {
|
|
1676
|
+
const virtualType = this.getVirtualFieldType(virtual);
|
|
1677
|
+
return virtualType === this.selectedType;
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
return virtuals;
|
|
1682
|
+
},
|
|
1683
|
+
formattedJson() {
|
|
1684
|
+
if (!this.document) {
|
|
1685
|
+
return '{}';
|
|
1686
|
+
}
|
|
1687
|
+
return JSON.stringify(this.document, null, 2);
|
|
1688
|
+
}
|
|
1689
|
+
},
|
|
1690
|
+
methods: {
|
|
1691
|
+
toggleVirtualField(fieldName) {
|
|
1692
|
+
if (this.collapsedVirtuals.has(fieldName)) {
|
|
1693
|
+
this.collapsedVirtuals.delete(fieldName);
|
|
1694
|
+
} else {
|
|
1695
|
+
this.collapsedVirtuals.add(fieldName);
|
|
1696
|
+
}
|
|
1697
|
+
},
|
|
1698
|
+
isVirtualFieldCollapsed(fieldName) {
|
|
1699
|
+
return this.collapsedVirtuals.has(fieldName);
|
|
1700
|
+
},
|
|
1701
|
+
openAddFieldModal() {
|
|
1702
|
+
this.showAddFieldModal = true;
|
|
1703
|
+
this.$nextTick(() => {
|
|
1704
|
+
if (this.shouldUseCodeMirror) {
|
|
1705
|
+
this.initializeFieldValueEditor();
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
},
|
|
1709
|
+
closeAddFieldModal() {
|
|
1710
|
+
this.showAddFieldModal = false;
|
|
1711
|
+
this.destroyFieldValueEditor();
|
|
1712
|
+
this.resetFieldForm();
|
|
1713
|
+
},
|
|
1714
|
+
async addNewField(fieldData) {
|
|
1715
|
+
// Emit event to parent component to handle the field addition
|
|
1716
|
+
this.$emit('add-field', fieldData);
|
|
1717
|
+
this.closeAddFieldModal();
|
|
1718
|
+
},
|
|
1719
|
+
validateFieldForm() {
|
|
1720
|
+
this.fieldErrors = {};
|
|
1721
|
+
|
|
1722
|
+
// Validate field name
|
|
1723
|
+
const trimmedName = this.fieldData.name.trim();
|
|
1724
|
+
if (!trimmedName) {
|
|
1725
|
+
this.fieldErrors.name = 'Field name is required';
|
|
1726
|
+
} else {
|
|
1727
|
+
const transformedName = this.getTransformedFieldName();
|
|
1728
|
+
if (!transformedName) {
|
|
1729
|
+
this.fieldErrors.name = 'Field name contains only invalid characters';
|
|
1730
|
+
} else if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(transformedName)) {
|
|
1731
|
+
this.fieldErrors.name = 'Field name must start with a letter, underscore, or $ and contain only letters, numbers, underscores, and $';
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// Validate field type
|
|
1736
|
+
if (!this.fieldData.type) {
|
|
1737
|
+
this.fieldErrors.type = 'Field type is required';
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// Validate field value if provided
|
|
1741
|
+
if (this.fieldData.value && this.fieldData.value.trim()) {
|
|
1742
|
+
if (['Object', 'Array'].includes(this.fieldData.type)) {
|
|
1743
|
+
try {
|
|
1744
|
+
JSON.parse(this.fieldData.value);
|
|
1745
|
+
} catch (e) {
|
|
1746
|
+
this.fieldErrors.value = 'Invalid JSON format for object/array type';
|
|
1747
|
+
}
|
|
1748
|
+
} else if (this.fieldData.type === 'Number') {
|
|
1749
|
+
if (isNaN(Number(this.fieldData.value))) {
|
|
1750
|
+
this.fieldErrors.value = 'Invalid number format';
|
|
1751
|
+
}
|
|
1752
|
+
} else if (this.fieldData.type === 'Boolean') {
|
|
1753
|
+
const lowerValue = this.fieldData.value.toLowerCase();
|
|
1754
|
+
if (!['true', 'false', '1', '0', 'yes', 'no'].includes(lowerValue)) {
|
|
1755
|
+
this.fieldErrors.value = 'Invalid boolean value (use true/false, 1/0, yes/no)';
|
|
1756
|
+
}
|
|
1757
|
+
} else if (this.fieldData.type === 'Date') {
|
|
1758
|
+
// Date picker provides YYYY-MM-DD format, validate it
|
|
1759
|
+
const dateValue = new Date(this.fieldData.value);
|
|
1760
|
+
if (isNaN(dateValue.getTime())) {
|
|
1761
|
+
this.fieldErrors.value = 'Invalid date format';
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
return Object.keys(this.fieldErrors).length === 0;
|
|
1767
|
+
},
|
|
1768
|
+
parseFieldValue(value, type) {
|
|
1769
|
+
if (!value || !value.trim()) {
|
|
1770
|
+
return null;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
switch (type) {
|
|
1774
|
+
case 'Number':
|
|
1775
|
+
return Number(value);
|
|
1776
|
+
case 'Boolean':
|
|
1777
|
+
const lowerValue = value.toLowerCase();
|
|
1778
|
+
return ['true', '1', 'yes'].includes(lowerValue);
|
|
1779
|
+
case 'Date':
|
|
1780
|
+
// For date picker, value is already in YYYY-MM-DD format
|
|
1781
|
+
return new Date(value);
|
|
1782
|
+
case 'Object':
|
|
1783
|
+
case 'Array':
|
|
1784
|
+
return JSON.parse(value);
|
|
1785
|
+
default:
|
|
1786
|
+
return value;
|
|
1787
|
+
}
|
|
1788
|
+
},
|
|
1789
|
+
async handleAddFieldSubmit() {
|
|
1790
|
+
if (!this.validateFieldForm()) {
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
this.isSubmittingField = true;
|
|
1795
|
+
|
|
1796
|
+
try {
|
|
1797
|
+
const fieldData = {
|
|
1798
|
+
name: this.getTransformedFieldName(),
|
|
1799
|
+
type: this.fieldData.type,
|
|
1800
|
+
value: this.parseFieldValue(this.fieldData.value, this.fieldData.type)
|
|
1801
|
+
};
|
|
1802
|
+
|
|
1803
|
+
this.$emit('add-field', fieldData);
|
|
1804
|
+
this.closeAddFieldModal();
|
|
1805
|
+
} catch (error) {
|
|
1806
|
+
console.error('Error adding field:', error);
|
|
1807
|
+
this.fieldErrors.value = 'Error processing field value';
|
|
1808
|
+
} finally {
|
|
1809
|
+
this.isSubmittingField = false;
|
|
1810
|
+
}
|
|
1811
|
+
},
|
|
1812
|
+
resetFieldForm() {
|
|
1813
|
+
this.fieldData = {
|
|
1814
|
+
name: '',
|
|
1815
|
+
type: '',
|
|
1816
|
+
value: ''
|
|
1817
|
+
};
|
|
1818
|
+
this.fieldErrors = {};
|
|
1819
|
+
this.isSubmittingField = false;
|
|
1820
|
+
// Reset CodeMirror editor if it exists
|
|
1821
|
+
if (this.fieldValueEditor) {
|
|
1822
|
+
this.fieldValueEditor.setValue('');
|
|
1823
|
+
}
|
|
1824
|
+
},
|
|
1825
|
+
initializeFieldValueEditor() {
|
|
1826
|
+
if (this.$refs.fieldValueEditor && !this.fieldValueEditor && this.shouldUseCodeMirror) {
|
|
1827
|
+
this.$refs.fieldValueEditor.value = this.fieldData.value || '';
|
|
1828
|
+
this.fieldValueEditor = CodeMirror.fromTextArea(this.$refs.fieldValueEditor, {
|
|
1829
|
+
mode: 'javascript',
|
|
1830
|
+
lineNumbers: true,
|
|
1831
|
+
height: 'auto'
|
|
1832
|
+
});
|
|
1833
|
+
this.fieldValueEditor.on('change', () => {
|
|
1834
|
+
this.fieldData.value = this.fieldValueEditor.getValue();
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
},
|
|
1838
|
+
destroyFieldValueEditor() {
|
|
1839
|
+
if (this.fieldValueEditor) {
|
|
1840
|
+
this.fieldValueEditor.toTextArea();
|
|
1841
|
+
this.fieldValueEditor = null;
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
toSnakeCase(str) {
|
|
1845
|
+
return str
|
|
1846
|
+
.trim()
|
|
1847
|
+
.replace(/\s+/g, '_') // Replace spaces with underscores
|
|
1848
|
+
.replace(/[^a-zA-Z0-9_$]/g, '') // Remove invalid characters
|
|
1849
|
+
.replace(/^[0-9]/, '_$&') // Prefix numbers with underscore
|
|
1850
|
+
.toLowerCase();
|
|
1851
|
+
},
|
|
1852
|
+
getTransformedFieldName() {
|
|
1853
|
+
if (!this.fieldData.name) return '';
|
|
1854
|
+
return this.toSnakeCase(this.fieldData.name.trim());
|
|
1855
|
+
},
|
|
1856
|
+
getVirtualFieldType(virtual) {
|
|
1857
|
+
const value = virtual.value;
|
|
1858
|
+
if (value === null || value === undefined) {
|
|
1859
|
+
return 'null';
|
|
1860
|
+
}
|
|
1861
|
+
if (Array.isArray(value)) {
|
|
1862
|
+
return 'Array';
|
|
1863
|
+
}
|
|
1864
|
+
if (value instanceof Date) {
|
|
1865
|
+
return 'Date';
|
|
1866
|
+
}
|
|
1867
|
+
if (typeof value === 'object') {
|
|
1868
|
+
return 'Object';
|
|
1869
|
+
}
|
|
1870
|
+
if (typeof value === 'number') {
|
|
1871
|
+
return 'Number';
|
|
1872
|
+
}
|
|
1873
|
+
if (typeof value === 'boolean') {
|
|
1874
|
+
return 'Boolean';
|
|
1875
|
+
}
|
|
1876
|
+
if (typeof value === 'string') {
|
|
1877
|
+
return 'String';
|
|
1878
|
+
}
|
|
1879
|
+
return 'unknown';
|
|
1549
1880
|
}
|
|
1550
1881
|
}
|
|
1551
1882
|
});
|
|
1552
1883
|
|
|
1884
|
+
|
|
1553
1885
|
/***/ }),
|
|
1554
1886
|
|
|
1555
1887
|
/***/ "./frontend/src/document-details/document-property/document-property.js":
|
|
@@ -1572,10 +1904,41 @@ module.exports = app => app.component('document-property', {
|
|
|
1572
1904
|
template,
|
|
1573
1905
|
data: function() {
|
|
1574
1906
|
return {
|
|
1575
|
-
dateType: 'picker' // picker, iso
|
|
1907
|
+
dateType: 'picker', // picker, iso
|
|
1908
|
+
isCollapsed: false, // Start uncollapsed by default
|
|
1909
|
+
isValueExpanded: false // Track if the value is expanded
|
|
1576
1910
|
};
|
|
1577
1911
|
},
|
|
1578
1912
|
props: ['path', 'document', 'schemaPaths', 'editting', 'changes', 'invalid'],
|
|
1913
|
+
computed: {
|
|
1914
|
+
valueAsString() {
|
|
1915
|
+
const value = this.getValueForPath(this.path.path);
|
|
1916
|
+
if (value == null) {
|
|
1917
|
+
return String(value);
|
|
1918
|
+
}
|
|
1919
|
+
if (typeof value === 'object') {
|
|
1920
|
+
return JSON.stringify(value, null, 2);
|
|
1921
|
+
}
|
|
1922
|
+
return String(value);
|
|
1923
|
+
},
|
|
1924
|
+
needsTruncation() {
|
|
1925
|
+
// Truncate if value is longer than 200 characters
|
|
1926
|
+
return this.valueAsString.length > 200;
|
|
1927
|
+
},
|
|
1928
|
+
displayValue() {
|
|
1929
|
+
if (!this.needsTruncation || this.isValueExpanded) {
|
|
1930
|
+
return this.getValueForPath(this.path.path);
|
|
1931
|
+
}
|
|
1932
|
+
// Return truncated value - we'll handle this in the template
|
|
1933
|
+
return this.getValueForPath(this.path.path);
|
|
1934
|
+
},
|
|
1935
|
+
truncatedString() {
|
|
1936
|
+
if (this.needsTruncation && !this.isValueExpanded) {
|
|
1937
|
+
return this.valueAsString.substring(0, 200) + '...';
|
|
1938
|
+
}
|
|
1939
|
+
return this.valueAsString;
|
|
1940
|
+
}
|
|
1941
|
+
},
|
|
1579
1942
|
methods: {
|
|
1580
1943
|
getComponentForPath(schemaPath) {
|
|
1581
1944
|
if (schemaPath.instance === 'Array') {
|
|
@@ -1596,6 +1959,9 @@ module.exports = app => app.component('document-property', {
|
|
|
1596
1959
|
if (path.instance === 'Embedded') {
|
|
1597
1960
|
return 'edit-subdocument';
|
|
1598
1961
|
}
|
|
1962
|
+
if (path.instance === 'Boolean') {
|
|
1963
|
+
return 'edit-boolean';
|
|
1964
|
+
}
|
|
1599
1965
|
return 'edit-default';
|
|
1600
1966
|
},
|
|
1601
1967
|
getValueForPath(path) {
|
|
@@ -1611,7 +1977,14 @@ module.exports = app => app.component('document-property', {
|
|
|
1611
1977
|
if (!this.document) {
|
|
1612
1978
|
return;
|
|
1613
1979
|
}
|
|
1614
|
-
|
|
1980
|
+
const documentValue = mpath.get(path, this.document);
|
|
1981
|
+
return documentValue;
|
|
1982
|
+
},
|
|
1983
|
+
toggleCollapse() {
|
|
1984
|
+
this.isCollapsed = !this.isCollapsed;
|
|
1985
|
+
},
|
|
1986
|
+
toggleValueExpansion() {
|
|
1987
|
+
this.isValueExpanded = !this.isValueExpanded;
|
|
1615
1988
|
}
|
|
1616
1989
|
}
|
|
1617
1990
|
});
|
|
@@ -1715,13 +2088,16 @@ module.exports = app => app.component('document', {
|
|
|
1715
2088
|
invalid: {},
|
|
1716
2089
|
editting: false,
|
|
1717
2090
|
virtuals: [],
|
|
2091
|
+
virtualPaths: [],
|
|
2092
|
+
mobileMenuOpen: false,
|
|
2093
|
+
viewMode: 'fields',
|
|
1718
2094
|
shouldShowConfirmModal: false,
|
|
1719
2095
|
shouldShowDeleteModal: false,
|
|
1720
2096
|
shouldShowCloneModal: false
|
|
1721
2097
|
}),
|
|
1722
2098
|
async mounted() {
|
|
1723
2099
|
window.pageState = this;
|
|
1724
|
-
const { doc, schemaPaths } = await api.Model.getDocument({ model: this.model, documentId: this.documentId });
|
|
2100
|
+
const { doc, schemaPaths, virtualPaths } = await api.Model.getDocument({ model: this.model, documentId: this.documentId });
|
|
1725
2101
|
window.doc = doc;
|
|
1726
2102
|
this.document = doc;
|
|
1727
2103
|
this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
|
|
@@ -1733,6 +2109,7 @@ module.exports = app => app.component('document', {
|
|
|
1733
2109
|
}
|
|
1734
2110
|
return 0;
|
|
1735
2111
|
}).map(key => schemaPaths[key]);
|
|
2112
|
+
this.virtualPaths = virtualPaths || [];
|
|
1736
2113
|
this.status = 'loaded';
|
|
1737
2114
|
},
|
|
1738
2115
|
computed: {
|
|
@@ -1744,6 +2121,9 @@ module.exports = app => app.component('document', {
|
|
|
1744
2121
|
return false;
|
|
1745
2122
|
}
|
|
1746
2123
|
return !this.roles.includes('readonly');
|
|
2124
|
+
},
|
|
2125
|
+
canEdit() {
|
|
2126
|
+
return this.canManipulate && this.viewMode === 'fields';
|
|
1747
2127
|
}
|
|
1748
2128
|
},
|
|
1749
2129
|
methods: {
|
|
@@ -1784,6 +2164,32 @@ module.exports = app => app.component('document', {
|
|
|
1784
2164
|
},
|
|
1785
2165
|
showClonedDocument(doc) {
|
|
1786
2166
|
this.$router.push({ path: `/model/${this.model}/document/${doc._id}` });
|
|
2167
|
+
},
|
|
2168
|
+
async addField(fieldData) {
|
|
2169
|
+
const { doc } = await api.Model.addField({
|
|
2170
|
+
model: this.model,
|
|
2171
|
+
_id: this.document._id,
|
|
2172
|
+
fieldName: fieldData.name,
|
|
2173
|
+
fieldValue: fieldData.value
|
|
2174
|
+
});
|
|
2175
|
+
this.document = doc;
|
|
2176
|
+
|
|
2177
|
+
// Show success message
|
|
2178
|
+
vanillatoast.create({
|
|
2179
|
+
title: 'Field Added!',
|
|
2180
|
+
text: `Field "${fieldData.name}" has been added to the document`,
|
|
2181
|
+
type: 'success',
|
|
2182
|
+
timeout: 3000,
|
|
2183
|
+
positionClass: 'bottomRight'
|
|
2184
|
+
});
|
|
2185
|
+
},
|
|
2186
|
+
updateViewMode(mode) {
|
|
2187
|
+
this.viewMode = mode;
|
|
2188
|
+
// Exit edit mode when switching to JSON view
|
|
2189
|
+
if (mode === 'json' && this.editting) {
|
|
2190
|
+
this.editting = false;
|
|
2191
|
+
this.changes = {};
|
|
2192
|
+
}
|
|
1787
2193
|
}
|
|
1788
2194
|
}
|
|
1789
2195
|
});
|
|
@@ -1853,6 +2259,55 @@ module.exports = app => app.component('edit-array', {
|
|
|
1853
2259
|
});
|
|
1854
2260
|
|
|
1855
2261
|
|
|
2262
|
+
/***/ }),
|
|
2263
|
+
|
|
2264
|
+
/***/ "./frontend/src/edit-boolean/edit-boolean.js":
|
|
2265
|
+
/*!***************************************************!*\
|
|
2266
|
+
!*** ./frontend/src/edit-boolean/edit-boolean.js ***!
|
|
2267
|
+
\***************************************************/
|
|
2268
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
2269
|
+
|
|
2270
|
+
"use strict";
|
|
2271
|
+
|
|
2272
|
+
|
|
2273
|
+
const template = __webpack_require__(/*! ./edit-boolean.html */ "./frontend/src/edit-boolean/edit-boolean.html");
|
|
2274
|
+
|
|
2275
|
+
module.exports = app => app.component('edit-boolean', {
|
|
2276
|
+
template: template,
|
|
2277
|
+
props: ['value'],
|
|
2278
|
+
emits: ['input'],
|
|
2279
|
+
data() {
|
|
2280
|
+
return {
|
|
2281
|
+
selectedValue: null
|
|
2282
|
+
};
|
|
2283
|
+
},
|
|
2284
|
+
mounted() {
|
|
2285
|
+
this.selectedValue = this.value;
|
|
2286
|
+
},
|
|
2287
|
+
watch: {
|
|
2288
|
+
value(newValue) {
|
|
2289
|
+
this.selectedValue = newValue;
|
|
2290
|
+
},
|
|
2291
|
+
selectedValue(newValue) {
|
|
2292
|
+
// Convert null/undefined to strings for proper backend serialization
|
|
2293
|
+
const emitValue = this.convertValueToString(newValue);
|
|
2294
|
+
this.$emit('input', emitValue);
|
|
2295
|
+
}
|
|
2296
|
+
},
|
|
2297
|
+
methods: {
|
|
2298
|
+
selectValue(value) {
|
|
2299
|
+
this.selectedValue = value;
|
|
2300
|
+
},
|
|
2301
|
+
convertValueToString(value) {
|
|
2302
|
+
// Convert null/undefined to strings for proper backend serialization
|
|
2303
|
+
if (value === null) return 'null';
|
|
2304
|
+
if (typeof value === 'undefined') return 'undefined';
|
|
2305
|
+
return value;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
});
|
|
2309
|
+
|
|
2310
|
+
|
|
1856
2311
|
/***/ }),
|
|
1857
2312
|
|
|
1858
2313
|
/***/ "./frontend/src/edit-date/edit-date.js":
|
|
@@ -2710,6 +3165,34 @@ module.exports = app => app.component('models', {
|
|
|
2710
3165
|
this.autocompleteTrie.bulkInsert(paths, 10);
|
|
2711
3166
|
}
|
|
2712
3167
|
},
|
|
3168
|
+
buildDocumentFetchParams(options = {}) {
|
|
3169
|
+
const params = {
|
|
3170
|
+
model: this.currentModel,
|
|
3171
|
+
limit
|
|
3172
|
+
};
|
|
3173
|
+
|
|
3174
|
+
if (typeof options.skip === 'number') {
|
|
3175
|
+
params.skip = options.skip;
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
const sortKeys = Object.keys(this.sortBy);
|
|
3179
|
+
if (sortKeys.length > 0) {
|
|
3180
|
+
const key = sortKeys[0];
|
|
3181
|
+
if (typeof key === 'string' && key.length > 0) {
|
|
3182
|
+
params.sortKey = key;
|
|
3183
|
+
const direction = this.sortBy[key];
|
|
3184
|
+
if (direction !== undefined && direction !== null) {
|
|
3185
|
+
params.sortDirection = direction;
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
if (typeof this.searchText === 'string' && this.searchText.trim().length > 0) {
|
|
3191
|
+
params.searchText = this.searchText;
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3194
|
+
return params;
|
|
3195
|
+
},
|
|
2713
3196
|
async initSearchFromUrl() {
|
|
2714
3197
|
this.status = 'loading';
|
|
2715
3198
|
this.query = Object.assign({}, this.$route.query); // important that this is here before the if statements
|
|
@@ -2753,13 +3236,48 @@ module.exports = app => app.component('models', {
|
|
|
2753
3236
|
const before = this.searchText.slice(0, cursorPos);
|
|
2754
3237
|
const match = before.match(/(?:\{|,)\s*([^:\s]*)$/);
|
|
2755
3238
|
if (match && match[1]) {
|
|
2756
|
-
const
|
|
3239
|
+
const token = match[1];
|
|
3240
|
+
const leadingQuoteMatch = token.match(/^["']/);
|
|
3241
|
+
const trailingQuoteMatch = token.length > 1 && /["']$/.test(token)
|
|
3242
|
+
? token[token.length - 1]
|
|
3243
|
+
: '';
|
|
3244
|
+
const term = token
|
|
3245
|
+
.replace(/^["']/, '')
|
|
3246
|
+
.replace(trailingQuoteMatch ? new RegExp(`[${trailingQuoteMatch}]$`) : '', '')
|
|
3247
|
+
.trim();
|
|
2757
3248
|
if (!term) {
|
|
2758
3249
|
this.autocompleteSuggestions = [];
|
|
2759
3250
|
return;
|
|
2760
3251
|
}
|
|
2761
3252
|
if (this.autocompleteTrie) {
|
|
2762
|
-
|
|
3253
|
+
const primarySuggestions = this.autocompleteTrie.getSuggestions(term, 10);
|
|
3254
|
+
const suggestionsSet = new Set(primarySuggestions);
|
|
3255
|
+
if (Array.isArray(this.schemaPaths) && this.schemaPaths.length > 0) {
|
|
3256
|
+
for (const schemaPath of this.schemaPaths) {
|
|
3257
|
+
const path = schemaPath?.path;
|
|
3258
|
+
if (
|
|
3259
|
+
typeof path === 'string' &&
|
|
3260
|
+
path.startsWith(`${term}.`) &&
|
|
3261
|
+
!suggestionsSet.has(path)
|
|
3262
|
+
) {
|
|
3263
|
+
suggestionsSet.add(path);
|
|
3264
|
+
if (suggestionsSet.size >= 10) {
|
|
3265
|
+
break;
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
let suggestions = Array.from(suggestionsSet);
|
|
3271
|
+
if (leadingQuoteMatch) {
|
|
3272
|
+
const leadingQuote = leadingQuoteMatch[0];
|
|
3273
|
+
suggestions = suggestions.map(suggestion => `${leadingQuote}${suggestion}`);
|
|
3274
|
+
}
|
|
3275
|
+
if (trailingQuoteMatch) {
|
|
3276
|
+
suggestions = suggestions.map(suggestion =>
|
|
3277
|
+
suggestion.endsWith(trailingQuoteMatch) ? suggestion : `${suggestion}${trailingQuoteMatch}`
|
|
3278
|
+
);
|
|
3279
|
+
}
|
|
3280
|
+
this.autocompleteSuggestions = suggestions;
|
|
2763
3281
|
this.autocompleteIndex = 0;
|
|
2764
3282
|
return;
|
|
2765
3283
|
}
|
|
@@ -2796,9 +3314,18 @@ module.exports = app => app.component('models', {
|
|
|
2796
3314
|
}
|
|
2797
3315
|
const token = match[1];
|
|
2798
3316
|
const start = cursorPos - token.length;
|
|
2799
|
-
|
|
3317
|
+
let replacement = suggestion;
|
|
3318
|
+
const leadingQuote = token.startsWith('"') || token.startsWith('\'') ? token[0] : '';
|
|
3319
|
+
const trailingQuote = token.length > 1 && (token.endsWith('"') || token.endsWith('\'')) ? token[token.length - 1] : '';
|
|
3320
|
+
if (leadingQuote && !replacement.startsWith(leadingQuote)) {
|
|
3321
|
+
replacement = `${leadingQuote}${replacement}`;
|
|
3322
|
+
}
|
|
3323
|
+
if (trailingQuote && !replacement.endsWith(trailingQuote)) {
|
|
3324
|
+
replacement = `${replacement}${trailingQuote}`;
|
|
3325
|
+
}
|
|
3326
|
+
this.searchText = this.searchText.slice(0, start) + replacement + after;
|
|
2800
3327
|
this.$nextTick(() => {
|
|
2801
|
-
const pos = start +
|
|
3328
|
+
const pos = start + replacement.length;
|
|
2802
3329
|
input.setSelectionRange(pos, pos);
|
|
2803
3330
|
});
|
|
2804
3331
|
this.autocompleteSuggestions = [];
|
|
@@ -2847,15 +3374,7 @@ module.exports = app => app.component('models', {
|
|
|
2847
3374
|
const container = this.$refs.documentsList;
|
|
2848
3375
|
if (container.scrollHeight - container.clientHeight - 100 < container.scrollTop) {
|
|
2849
3376
|
this.status = 'loading';
|
|
2850
|
-
const params = {
|
|
2851
|
-
model: this.currentModel,
|
|
2852
|
-
sort: this.sortBy,
|
|
2853
|
-
skip: this.documents.length,
|
|
2854
|
-
limit
|
|
2855
|
-
};
|
|
2856
|
-
if (typeof this.searchText === 'string' && this.searchText.trim().length > 0) {
|
|
2857
|
-
params.searchText = this.searchText;
|
|
2858
|
-
}
|
|
3377
|
+
const params = this.buildDocumentFetchParams({ skip: this.documents.length });
|
|
2859
3378
|
const { docs } = await api.Model.getDocuments(params);
|
|
2860
3379
|
if (docs.length < limit) {
|
|
2861
3380
|
this.loadedAllDocs = true;
|
|
@@ -2924,14 +3443,7 @@ module.exports = app => app.component('models', {
|
|
|
2924
3443
|
let schemaPathsReceived = false;
|
|
2925
3444
|
|
|
2926
3445
|
// Use async generator to stream SSEs
|
|
2927
|
-
const params =
|
|
2928
|
-
model: this.currentModel,
|
|
2929
|
-
sort: this.sortBy,
|
|
2930
|
-
limit
|
|
2931
|
-
};
|
|
2932
|
-
if (typeof this.searchText === 'string' && this.searchText.trim().length > 0) {
|
|
2933
|
-
params.searchText = this.searchText;
|
|
2934
|
-
}
|
|
3446
|
+
const params = this.buildDocumentFetchParams();
|
|
2935
3447
|
for await (const event of api.Model.getDocumentsStream(params)) {
|
|
2936
3448
|
if (event.schemaPaths && !schemaPathsReceived) {
|
|
2937
3449
|
// Sort schemaPaths with _id first
|
|
@@ -2975,15 +3487,7 @@ module.exports = app => app.component('models', {
|
|
|
2975
3487
|
let numDocsReceived = false;
|
|
2976
3488
|
|
|
2977
3489
|
// Use async generator to stream SSEs
|
|
2978
|
-
const params = {
|
|
2979
|
-
model: this.currentModel,
|
|
2980
|
-
sort: this.sortBy,
|
|
2981
|
-
skip: this.documents.length,
|
|
2982
|
-
limit
|
|
2983
|
-
};
|
|
2984
|
-
if (typeof this.searchText === 'string' && this.searchText.trim().length > 0) {
|
|
2985
|
-
params.searchText = this.searchText;
|
|
2986
|
-
}
|
|
3490
|
+
const params = this.buildDocumentFetchParams({ skip: this.documents.length });
|
|
2987
3491
|
for await (const event of api.Model.getDocumentsStream(params)) {
|
|
2988
3492
|
if (event.numDocs !== undefined && !numDocsReceived) {
|
|
2989
3493
|
this.numDocuments = event.numDocs;
|
|
@@ -3354,6 +3858,10 @@ exports.removeFromWorkspace = function removeFromWorkspace(params) {
|
|
|
3354
3858
|
return client.post('/removeFromWorkspace', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
|
|
3355
3859
|
};
|
|
3356
3860
|
|
|
3861
|
+
exports.updateWorkspaceMember = function updateWorkspaceMember(params) {
|
|
3862
|
+
return client.post('/updateWorkspaceMember', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
|
|
3863
|
+
};
|
|
3864
|
+
|
|
3357
3865
|
exports.hasAPIKey = client.hasAPIKey;
|
|
3358
3866
|
|
|
3359
3867
|
|
|
@@ -3661,6 +4169,7 @@ module.exports = app => app.component('team', {
|
|
|
3661
4169
|
invitations: null,
|
|
3662
4170
|
showNewInvitationModal: false,
|
|
3663
4171
|
showRemoveModal: null,
|
|
4172
|
+
showEditModal: null,
|
|
3664
4173
|
status: 'loading'
|
|
3665
4174
|
}),
|
|
3666
4175
|
async mounted() {
|
|
@@ -3682,15 +4191,60 @@ module.exports = app => app.component('team', {
|
|
|
3682
4191
|
getRolesForUser(user) {
|
|
3683
4192
|
return this.workspace.members.find(member => member.userId === user._id)?.roles ?? [];
|
|
3684
4193
|
},
|
|
4194
|
+
openEditModal(user) {
|
|
4195
|
+
if (this.getRolesForUser(user).includes('owner')) {
|
|
4196
|
+
return;
|
|
4197
|
+
}
|
|
4198
|
+
|
|
4199
|
+
const roles = this.getRolesForUser(user);
|
|
4200
|
+
const nonOwnerRoles = roles.filter(role => role !== 'owner');
|
|
4201
|
+
const currentRole = nonOwnerRoles[0] ?? null;
|
|
4202
|
+
const editableRole = currentRole ?? (this.workspace?.subscriptionTier ? 'member' : 'dashboards');
|
|
4203
|
+
|
|
4204
|
+
this.showEditModal = {
|
|
4205
|
+
user,
|
|
4206
|
+
role: editableRole,
|
|
4207
|
+
originalRole: currentRole
|
|
4208
|
+
};
|
|
4209
|
+
},
|
|
4210
|
+
closeEditModal() {
|
|
4211
|
+
this.showEditModal = null;
|
|
4212
|
+
},
|
|
4213
|
+
async updateWorkspaceMember() {
|
|
4214
|
+
if (this.showEditModal.role === this.showEditModal.originalRole) {
|
|
4215
|
+
this.closeEditModal();
|
|
4216
|
+
return;
|
|
4217
|
+
}
|
|
4218
|
+
|
|
4219
|
+
const { workspace, users } = await mothership.updateWorkspaceMember({
|
|
4220
|
+
userId: this.showEditModal.user._id,
|
|
4221
|
+
roles: [this.showEditModal.role]
|
|
4222
|
+
});
|
|
4223
|
+
|
|
4224
|
+
this.workspace = workspace;
|
|
4225
|
+
this.users = users;
|
|
4226
|
+
this.closeEditModal();
|
|
4227
|
+
},
|
|
3685
4228
|
async removeFromWorkspace() {
|
|
3686
4229
|
const { workspace, users } = await mothership.removeFromWorkspace({ userId: this.showRemoveModal._id });
|
|
3687
4230
|
this.workspace = workspace;
|
|
3688
4231
|
this.users = users;
|
|
3689
|
-
this.showRemoveModal =
|
|
4232
|
+
this.showRemoveModal = null;
|
|
3690
4233
|
},
|
|
3691
4234
|
async getWorkspaceCustomerPortalLink() {
|
|
3692
4235
|
const { url } = await mothership.getWorkspaceCustomerPortalLink();
|
|
3693
4236
|
window.open(url, '_self');
|
|
4237
|
+
},
|
|
4238
|
+
disableRoleOption(option) {
|
|
4239
|
+
if (this.workspace?.subscriptionTier) {
|
|
4240
|
+
return false;
|
|
4241
|
+
}
|
|
4242
|
+
|
|
4243
|
+
if (this.showEditModal?.originalRole === option) {
|
|
4244
|
+
return false;
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
return option !== 'dashboards';
|
|
3694
4248
|
}
|
|
3695
4249
|
}
|
|
3696
4250
|
});
|
|
@@ -3871,6 +4425,9 @@ var map = {
|
|
|
3871
4425
|
"./edit-array/edit-array.css": "./frontend/src/edit-array/edit-array.css",
|
|
3872
4426
|
"./edit-array/edit-array.html": "./frontend/src/edit-array/edit-array.html",
|
|
3873
4427
|
"./edit-array/edit-array.js": "./frontend/src/edit-array/edit-array.js",
|
|
4428
|
+
"./edit-boolean/edit-boolean": "./frontend/src/edit-boolean/edit-boolean.js",
|
|
4429
|
+
"./edit-boolean/edit-boolean.html": "./frontend/src/edit-boolean/edit-boolean.html",
|
|
4430
|
+
"./edit-boolean/edit-boolean.js": "./frontend/src/edit-boolean/edit-boolean.js",
|
|
3874
4431
|
"./edit-date/edit-date": "./frontend/src/edit-date/edit-date.js",
|
|
3875
4432
|
"./edit-date/edit-date.html": "./frontend/src/edit-date/edit-date.html",
|
|
3876
4433
|
"./edit-date/edit-date.js": "./frontend/src/edit-date/edit-date.js",
|
|
@@ -4808,7 +5365,7 @@ module.exports = "<div>\n {{value}}\n</div>";
|
|
|
4808
5365
|
/***/ ((module) => {
|
|
4809
5366
|
|
|
4810
5367
|
"use strict";
|
|
4811
|
-
module.exports = ".document-details {\n width: 100%;\n}\n\n.document-details .value {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n\n.document-details .path-key {\n background-color: #f0f0f0;\n margin-bottom: 0.5em;\n}\n\n.document-details .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n}\n\n.document-details .date-position {\n float: right;\n margin-top: -7px;\n}";
|
|
5368
|
+
module.exports = ".document-details {\n width: 100%;\n}\n\n.document-details .value {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n\n.document-details .path-key {\n background-color: #f0f0f0;\n margin-bottom: 0.5em;\n}\n\n.document-details .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n}\n\n.document-details .date-position {\n float: right;\n margin-top: -7px;\n}\n\n/* Add Field Modal Styles */\n.add-field-modal {\n max-width: 500px;\n width: 100%;\n}\n\n.add-field-modal .modal-exit {\n position: absolute;\n top: 15px;\n right: 20px;\n font-size: 24px;\n cursor: pointer;\n color: #6b7280;\n z-index: 10;\n}\n\n.add-field-modal .modal-exit:hover {\n color: #374151;\n}\n\n.add-field-modal form {\n max-height: 70vh;\n overflow-y: auto;\n}\n\n.add-field-modal input[type=\"text\"],\n.add-field-modal input[type=\"email\"],\n.add-field-modal input[type=\"password\"],\n.add-field-modal select,\n.add-field-modal textarea {\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.add-field-modal input:focus,\n.add-field-modal select:focus,\n.add-field-modal textarea:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n.add-field-modal .border-red-500 {\n border-color: #ef4444 !important;\n}\n\n.add-field-modal .border-red-500:focus {\n box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1) !important;\n}\n\n/* CodeMirror styling in modal */\n.add-field-modal .CodeMirror {\n border: 1px solid #d1d5db;\n border-radius: 0.375rem;\n font-size: 14px;\n height: auto;\n min-height: 100px;\n}\n\n.add-field-modal .CodeMirror:focus-within {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n.add-field-modal .CodeMirror.CodeMirror-focused {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n/* JSON View Styles */\n.json-view {\n width: 100%;\n}\n\n.json-view pre {\n margin: 0;\n max-height: 70vh;\n overflow: auto;\n line-height: 1.5;\n}\n\n.json-view pre::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n.json-view pre::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 4px;\n}\n\n.json-view pre::-webkit-scrollbar-thumb {\n background: #888;\n border-radius: 4px;\n}\n\n.json-view pre::-webkit-scrollbar-thumb:hover {\n background: #555;\n}";
|
|
4812
5369
|
|
|
4813
5370
|
/***/ }),
|
|
4814
5371
|
|
|
@@ -4819,7 +5376,7 @@ module.exports = ".document-details {\n width: 100%;\n}\n\n.document-details .v
|
|
|
4819
5376
|
/***/ ((module) => {
|
|
4820
5377
|
|
|
4821
5378
|
"use strict";
|
|
4822
|
-
module.exports = "<div class=\"document-details\">\n <div v-for=\"path in schemaPaths\" class=\"value\">\n <document-property\n :path=\"path\"\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"></document-property>\n </div>\n <div v-for=\"path in virtuals\" class=\"mb-2\">\n <div class=\"p-1 mb-1 bg-slate-100\">\n {{path.name}}\n <span class=\"path-type\">\n (virtual)\n </span>\n </div>\n <div v-if=\"path.value == null\" class=\"text-sky-800\">\n {{'' + path.value}}\n </div>\n <div v-else>\n {{path.value}}\n </div>\n </div>\n</div>";
|
|
5379
|
+
module.exports = "<div class=\"document-details\">\n <!-- View Toggle and Search/Filter Bar -->\n <div class=\"mb-4 mt-4\">\n\n <!-- Search and Filter Bar (only show in fields view) -->\n <div v-if=\"viewMode === 'fields'\" class=\"flex md:gap-3\">\n <!-- Search Bar -->\n <div class=\"relative flex-1\">\n <input\n ref=\"searchInput\"\n v-model=\"searchQuery\"\n type=\"text\"\n placeholder=\"Search fields...\"\n class=\"w-full px-4 py-2 pl-10 pr-4 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n />\n <div class=\"absolute inset-y-0 left-0 flex items-center pl-3\">\n <svg class=\"w-4 h-4 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"></path>\n </svg>\n </div>\n </div>\n\n <!-- Data Type Filter -->\n <div class=\"relative\">\n <select\n v-model=\"selectedType\"\n class=\"hidden md:block px-4 py-2 pr-8 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white min-w-[140px] appearance-none\"\n >\n <option value=\"\">All Types</option>\n <option v-for=\"type in availableTypes\" :key=\"type\" :value=\"type\">\n {{type}}\n </option>\n </select>\n <div class=\"absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none\">\n <svg class=\"w-4 h-4 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n </div>\n </div>\n\n <!-- Add Field Button -->\n <button\n @click=\"openAddFieldModal\"\n class=\"hidden md:block px-4 py-2 text-sm font-medium text-white bg-green-600 hover:bg-green-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 flex items-center gap-2\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\"></path>\n </svg>\n Add Field\n </button>\n </div>\n </div>\n\n <!-- Fields View -->\n <div v-if=\"viewMode === 'fields'\">\n <!-- Schema Paths -->\n <div v-for=\"path in filteredSchemaPaths\" :key=\"path\" class=\"value\">\n <document-property\n :path=\"path\"\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"></document-property>\n </div>\n\n <!-- Virtual Fields -->\n <div v-for=\"path in filteredVirtuals\" class=\"border border-gray-200 rounded-lg mb-2\">\n <!-- Virtual Field Header (Always Visible) -->\n <div\n @click=\"toggleVirtualField(path.name)\"\n class=\"p-3 bg-slate-50 hover:bg-slate-100 cursor-pointer flex items-center justify-between border-b border-gray-200\"\n >\n <div class=\"flex items-center\">\n <svg\n :class=\"isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-gray-500 mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-gray-900\">{{path.name}}</span>\n <span v-if=\"path.isVirtual\" class=\"ml-2 text-sm text-purple-600\">(virtual - {{getVirtualFieldType(path)}})</span>\n <span v-else class=\"ml-2 text-sm text-blue-600\">(user-added - {{getVirtualFieldType(path)}})</span>\n </div>\n </div>\n\n <!-- Virtual Field Content (Collapsible) -->\n <div v-if=\"!isVirtualFieldCollapsed(path.name)\" class=\"p-3\">\n <div v-if=\"path.value == null\" class=\"text-sky-800\">\n {{'' + path.value}}\n </div>\n <div v-else>\n {{path.value}}\n </div>\n </div>\n </div>\n\n <!-- No Results Message -->\n <div v-if=\"searchQuery && filteredSchemaPaths.length === 0 && filteredVirtuals.length === 0\" class=\"text-center py-8 text-gray-500\">\n <svg class=\"w-12 h-12 mx-auto mb-4 text-gray-300\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.29-1.009-5.824-2.709M15 6.291A7.962 7.962 0 0012 5c-2.34 0-4.29 1.009-5.824 2.709\"></path>\n </svg>\n <p>No fields found matching \"{{searchQuery}}\"</p>\n </div>\n </div>\n\n <!-- JSON View -->\n <div v-if=\"viewMode === 'json'\" class=\"json-view\">\n <div class=\"border border-gray-300 rounded-lg bg-gray-50 p-4 overflow-auto\">\n <pre class=\"text-sm font-mono text-gray-800 whitespace-pre\">{{formattedJson}}</pre>\n </div>\n </div>\n\n <!-- Add Field Modal -->\n <modal v-if=\"showAddFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"closeAddFieldModal\">×</div>\n <div class=\"add-field-modal\">\n <div class=\"mb-6\">\n <h2 class=\"text-xl font-semibold text-gray-900 mb-2\">Add New Field</h2>\n <p class=\"text-sm text-gray-600\">Create a new field for this document</p>\n </div>\n\n <form @submit.prevent=\"handleAddFieldSubmit\" class=\"space-y-4\">\n <!-- Field Name -->\n <div>\n <label for=\"fieldName\" class=\"block text-sm font-medium text-gray-700 mb-1\">\n Field Name *\n </label>\n <input\n id=\"fieldName\"\n v-model=\"fieldData.name\"\n type=\"text\"\n required\n placeholder=\"Enter field name...\"\n class=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.name }\"\n />\n <p v-if=\"fieldErrors.name\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.name}}</p>\n <p v-if=\"fieldData.name && getTransformedFieldName() !== fieldData.name.trim()\" class=\"mt-1 text-sm text-blue-600\">\n Field name will be: <code class=\"bg-blue-50 px-1 py-0.5 rounded text-xs\">{{getTransformedFieldName()}}</code>\n </p>\n </div>\n\n <!-- Field Type -->\n <div>\n <label for=\"fieldType\" class=\"block text-sm font-medium text-gray-700 mb-1\">\n Field Type *\n </label>\n <select\n id=\"fieldType\"\n v-model=\"fieldData.type\"\n required\n class=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.type }\"\n >\n <option value=\"\">Select field type...</option>\n <option v-for=\"type in allFieldTypes\" :key=\"type\" :value=\"type\">\n {{type}}\n </option>\n </select>\n <p v-if=\"fieldErrors.type\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.type}}</p>\n </div>\n\n <!-- Field Value -->\n <div>\n <label for=\"fieldValue\" class=\"block text-sm font-medium text-gray-700 mb-1\">\n Initial Value\n </label>\n\n <!-- Date picker for Date type -->\n <input\n v-if=\"shouldUseDatePicker\"\n v-model=\"fieldData.value\"\n type=\"date\"\n id=\"fieldValue\"\n class=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n />\n\n <!-- Simple input for basic types -->\n <input\n v-else-if=\"!shouldUseCodeMirror\"\n v-model=\"fieldData.value\"\n type=\"text\"\n id=\"fieldValue\"\n placeholder=\"Enter initial value (optional)...\"\n class=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n />\n\n <!-- CodeMirror textarea for complex types -->\n <textarea\n v-else\n ref=\"fieldValueEditor\"\n id=\"fieldValue\"\n rows=\"3\"\n placeholder=\"Enter initial value (optional)...\"\n class=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n ></textarea>\n\n <p v-if=\"fieldErrors.value\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.value}}</p>\n <p class=\"mt-1 text-xs text-gray-500\">\n <span v-if=\"shouldUseDatePicker\">Select a date or leave empty for null/undefined values.</span>\n <span v-else-if=\"shouldUseCodeMirror\">Leave empty for null/undefined values. Use valid JSON format.</span>\n <span v-else>Leave empty for null/undefined values.</span>\n </p>\n </div>\n\n\n <!-- Action Buttons -->\n <div class=\"flex justify-end gap-3 pt-4 border-t border-gray-200\">\n <button\n type=\"button\"\n @click=\"closeAddFieldModal\"\n class=\"px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n :disabled=\"isSubmittingField\"\n class=\"px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <span v-if=\"isSubmittingField\">Adding...</span>\n <span v-else>Add Field</span>\n </button>\n </div>\n </form>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
4823
5380
|
|
|
4824
5381
|
/***/ }),
|
|
4825
5382
|
|
|
@@ -4830,7 +5387,7 @@ module.exports = "<div class=\"document-details\">\n <div v-for=\"path in schem
|
|
|
4830
5387
|
/***/ ((module) => {
|
|
4831
5388
|
|
|
4832
5389
|
"use strict";
|
|
4833
|
-
module.exports = ".document-details {\n width: 100%;\n }\n \n .document-details .value {\n padding-top: 10px;\n padding-bottom: 10px;\n }\n \n .document-details .path-key {\n background-color: #f0f0f0;\n margin-bottom: 0.5em;\n }\n \n .document-details .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n }\n \n .document-details .date-position {\n float: right;\n margin-top: -7px;\n }";
|
|
5390
|
+
module.exports = ".document-details {\n width: 100%;\n }\n \n .document-details .value {\n padding-top: 10px;\n padding-bottom: 10px;\n }\n \n .document-details .path-key {\n background-color: #f0f0f0;\n margin-bottom: 0.5em;\n }\n \n .document-details .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n }\n \n .document-details .date-position {\n float: right;\n margin-top: -7px;\n }\n\n .truncated-value-container,\n .expanded-value-container {\n position: relative;\n }\n\n .truncated-value-container button,\n .expanded-value-container button {\n transition: all 0.2s ease;\n }\n\n .truncated-value-container button:hover,\n .expanded-value-container button:hover {\n transform: translateX(2px);\n }";
|
|
4834
5391
|
|
|
4835
5392
|
/***/ }),
|
|
4836
5393
|
|
|
@@ -4841,7 +5398,7 @@ module.exports = ".document-details {\n width: 100%;\n }\n \n .document-de
|
|
|
4841
5398
|
/***/ ((module) => {
|
|
4842
5399
|
|
|
4843
5400
|
"use strict";
|
|
4844
|
-
module.exports = "<div>\n <div class=\"
|
|
5401
|
+
module.exports = "<div class=\"border border-gray-200 rounded-lg mb-2\">\n <!-- Collapsible Header -->\n <div \n @click=\"toggleCollapse\"\n class=\"p-3 bg-gray-50 hover:bg-gray-100 cursor-pointer flex items-center justify-between border-b border-gray-200\"\n >\n <div class=\"flex items-center\">\n <svg \n :class=\"isCollapsed ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-gray-500 mr-2 transition-transform duration-200\"\n fill=\"none\" \n stroke=\"currentColor\" \n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-gray-900\">{{path.path}}</span>\n <span class=\"ml-2 text-sm text-gray-500\">({{(path.instance || 'unknown').toLowerCase()}})</span>\n </div>\n <div class=\"flex items-center gap-2\">\n <router-link\n v-if=\"path.ref && getValueForPath(path.path)\"\n :to=\"`/model/${path.ref}/document/${getValueForPath(path.path)}`\"\n class=\"bg-ultramarine-600 hover:bg-ultramarine-500 text-white px-2 py-1 text-sm rounded-md\"\n @click.stop\n >View Document\n </router-link>\n </div>\n </div>\n \n <!-- Collapsible Content -->\n <div v-if=\"!isCollapsed\" class=\"p-3\">\n <!-- Date Type Selector (when editing dates) -->\n <div v-if=\"editting && path.instance === 'Date'\" class=\"mb-3 flex gap-1.5\">\n <div\n @click=\"dateType = 'picker'\"\n :class=\"dateType === 'picker' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'picker' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n Date Picker\n </div>\n </div>\n <div\n @click=\"dateType = 'iso'\"\n :class=\"dateType === 'iso' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'iso' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n ISO String\n </div>\n </div>\n </div>\n \n <!-- Field Content -->\n <div v-if=\"editting && path.path !== '_id'\">\n <component\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :format=\"dateType\"\n @input=\"changes[path.path] = $event; delete invalid[path.path];\"\n @error=\"invalid[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else>\n <!-- Show truncated or full value based on needsTruncation and isValueExpanded -->\n <div v-if=\"needsTruncation && !isValueExpanded\" class=\"truncated-value-container\">\n <div class=\"text-gray-700 whitespace-pre-wrap break-words font-mono text-sm\">{{truncatedString}}</div>\n <button \n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show more ({{valueAsString.length}} characters)\n </button>\n </div>\n <div v-else-if=\"needsTruncation && isValueExpanded\" class=\"expanded-value-container\">\n <component :is=\"getComponentForPath(path)\" :value=\"getValueForPath(path.path)\"></component>\n <button \n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1\"\n >\n <svg class=\"w-4 h-4 rotate-180\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show less\n </button>\n </div>\n <div v-else>\n <component :is=\"getComponentForPath(path)\" :value=\"getValueForPath(path.path)\"></component>\n </div>\n </div>\n </div>\n</div>\n";
|
|
4845
5402
|
|
|
4846
5403
|
/***/ }),
|
|
4847
5404
|
|
|
@@ -4874,7 +5431,7 @@ module.exports = "<div>\n <h2>\n Are you sure you want to delete the f
|
|
|
4874
5431
|
/***/ ((module) => {
|
|
4875
5432
|
|
|
4876
5433
|
"use strict";
|
|
4877
|
-
module.exports = ".document {\n max-width: 1200px;\n margin-left: auto;\n margin-right: auto;\n padding-top: 25px;\n}\n\n.document .document-menu {\n display: flex;\n}\n\n.document .document-menu .left {\n flex-grow: 1;\n}\n\n.document .document-menu .right {\n flex-grow: 1;\n text-align: right;\n}\n\n.document .document-menu .right button:not(:last-child) {\n margin-right: 0.5em;\n}\n\n.document button img {\n height: 1em;\n}";
|
|
5434
|
+
module.exports = ".document {\n max-width: 1200px;\n margin-left: auto;\n margin-right: auto;\n padding-top: 25px;\n}\n\n.document .document-menu {\n display: flex;\n position: sticky;\n top: 0;\n z-index: 100;\n background-color: white;\n border-radius: 5px;\n padding: 15px 15px;\n margin: -15px 0 15px 0;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.document .document-menu .left {\n flex-grow: 1;\n}\n\n.document .document-menu .right {\n flex-grow: 1;\n text-align: right;\n}\n\n.document .document-menu .right button:not(:last-child) {\n margin-right: 0.5em;\n}\n\n.document button img {\n height: 1em;\n}";
|
|
4878
5435
|
|
|
4879
5436
|
/***/ }),
|
|
4880
5437
|
|
|
@@ -4885,7 +5442,7 @@ module.exports = ".document {\n max-width: 1200px;\n margin-left: auto;\n mar
|
|
|
4885
5442
|
/***/ ((module) => {
|
|
4886
5443
|
|
|
4887
5444
|
"use strict";
|
|
4888
|
-
module.exports = "<div class=\"document\">\n <div class=\"
|
|
5445
|
+
module.exports = "<div class=\"document px-1 md:px-0\">\n <div class=\"flex justify-between\">\n <div class=\"flex\">\n <button\n @click=\"$router.push('/model/' + this.model)\"\n class=\"mr-2 rounded-md bg-gray-400 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n ‹ Back\n </button>\n <button\n @click=\"viewMode = 'fields'\"\n :class=\"viewMode === 'fields'\n ? 'bg-blue-600 text-white z-10'\n : 'bg-gray-200 text-gray-700 hover:bg-gray-300'\"\n class=\"px-4 py-2 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-gray-300 border-r-0 rounded-l-lg rounded-r-none\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 10h16M4 14h16M4 18h16\"></path>\n </svg>\n Fields\n </button>\n <button\n @click=\"viewMode = 'json'\"\n :class=\"viewMode === 'json'\n ? 'bg-blue-600 text-white z-10'\n : 'bg-gray-200 text-gray-700 hover:bg-gray-300'\"\n class=\"px-4 py-2 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-gray-300 rounded-r-lg rounded-l-none\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4\"></path>\n </svg>\n JSON\n </button>\n </div>\n\n <div class=\"gap-2 hidden md:flex\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canEdit\"\n :class=\"{'cursor-not-allowed opacity-50': !canEdit}\"\n type=\"button\"\n class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n <button\n @click=\"shouldShowDeleteModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/delete.svg\" class=\"inline\" /> Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-pink-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/duplicate.svg\" class=\"inline\" /> Clone\n </button>\n </div>\n <div class=\"md:hidden flex items-center\">\n <div class=\"relative\">\n <button\n @click=\"mobileMenuOpen = !mobileMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"mobileMenuOpen\"\n aria-label=\"Open menu\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\n d=\"M4 6h16M4 12h16M4 18h16\"></path>\n </svg>\n </button>\n <div\n v-show=\"mobileMenuOpen\"\n @click.away=\"mobileMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-52 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true; mobileMenuOpen = false\"\n :disabled=\"!canEdit\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canEdit ? 'cursor-not-allowed opacity-50' : 'hover:bg-ultramarine-100']\"\n type=\"button\"\n >\n <img src=\"images/edit.svg\" class=\"inline mr-2\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false; mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-slate-100\"\n >\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-green-100']\"\n @click=\"shouldShowConfirmModal=true; mobileMenuOpen = false\"\n type=\"button\"\n >\n <img src=\"images/save.svg\" class=\"inline mr-2\" /> Save\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n <img src=\"images/delete.svg\" class=\"inline mr-2\" /> Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-gray-700', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n <img src=\"images/duplicate.svg\" class=\"inline mr-2\" /> Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :virtualPaths=\"virtualPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"\n :viewMode=\"viewMode\"\n @add-field=\"addField\"\n @view-mode-change=\"updateViewMode\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">×</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteModal = false;\">×</div>\n <confirm-delete @close=\"shouldShowDeleteModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">×</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n </div>\n</div>\n";
|
|
4889
5446
|
|
|
4890
5447
|
/***/ }),
|
|
4891
5448
|
|
|
@@ -4911,6 +5468,17 @@ module.exports = "<div class=\"edit-array\">\n <textarea\n ref=\"arrayEditor
|
|
|
4911
5468
|
|
|
4912
5469
|
/***/ }),
|
|
4913
5470
|
|
|
5471
|
+
/***/ "./frontend/src/edit-boolean/edit-boolean.html":
|
|
5472
|
+
/*!*****************************************************!*\
|
|
5473
|
+
!*** ./frontend/src/edit-boolean/edit-boolean.html ***!
|
|
5474
|
+
\*****************************************************/
|
|
5475
|
+
/***/ ((module) => {
|
|
5476
|
+
|
|
5477
|
+
"use strict";
|
|
5478
|
+
module.exports = "<div class=\"edit-boolean\">\n <div class=\"flex flex-wrap gap-2\">\n <label class=\"flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50\" \n :class=\"selectedValue === true ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'\">\n <input\n type=\"radio\"\n :checked=\"selectedValue === true\"\n @change=\"selectValue(true)\"\n class=\"w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500\"\n />\n <span class=\"text-sm font-medium\">true</span>\n </label>\n \n <label class=\"flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50\"\n :class=\"selectedValue === false ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'\">\n <input\n type=\"radio\"\n :checked=\"selectedValue === false\"\n @change=\"selectValue(false)\"\n class=\"w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500\"\n />\n <span class=\"text-sm font-medium\">false</span>\n </label>\n \n <label class=\"flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50\"\n :class=\"selectedValue === null ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'\">\n <input\n type=\"radio\"\n :checked=\"selectedValue === null\"\n @change=\"selectValue(null)\"\n class=\"w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500\"\n />\n <span class=\"text-sm font-medium\">null</span>\n </label>\n \n <label class=\"flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50\"\n :class=\"selectedValue === undefined ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'\">\n <input\n type=\"radio\"\n :checked=\"selectedValue === undefined\"\n @change=\"selectValue(undefined)\"\n class=\"w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500\"\n />\n <span class=\"text-sm font-medium\">undefined</span>\n </label>\n </div>\n</div>\n";
|
|
5479
|
+
|
|
5480
|
+
/***/ }),
|
|
5481
|
+
|
|
4914
5482
|
/***/ "./frontend/src/edit-date/edit-date.html":
|
|
4915
5483
|
/*!***********************************************!*\
|
|
4916
5484
|
!*** ./frontend/src/edit-date/edit-date.html ***!
|
|
@@ -5171,7 +5739,7 @@ module.exports = ".active {\n text-decoration: underline;\n}\n\n.navbar .nav-le
|
|
|
5171
5739
|
/***/ ((module) => {
|
|
5172
5740
|
|
|
5173
5741
|
"use strict";
|
|
5174
|
-
module.exports = "<div class=\"navbar w-full bg-gray-50 flex justify-between border-b border-gray-200 !h-[55px]\">\n <div class=\"flex items-center gap-4 h-full pl-4\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px] mr-1\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <div v-if=\"!!state.nodeEnv\" title=\"NODE_ENV\" class=\"inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900\" :class=\"warnEnv ? 'bg-red-300' : 'bg-yellow-300'\">\n {{state.nodeEnv}}\n </div>\n </div>\n <div class=\"h-full pr-4 hidden md:block\">\n <div class=\"sm:ml-6 sm:flex sm:space-x-8 h-full\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\"\n :class=\"documentView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Documents</a>\n <span v-else class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium text-gray-300 cursor-not-allowed\" aria-disabled=\"true\">\n Documents\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"dashboardView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Dashboards</a>\n <span v-else class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat')\"\n href=\"#/chat\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"chatView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Chat</a>\n <span v-else class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n\n <div class=\"h-full flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"h-full flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <div>\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800\" id=\"user-menu-button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <span class=\"absolute -inset-1.5\"></span>\n <span class=\"sr-only\">Open user menu</span>\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n </button>\n </div>\n\n <div v-if=\"showFlyout\" class=\"absolute right-0 z-10 top-[90%] w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\" role=\"menu\" aria-orientation=\"vertical\" aria-labelledby=\"user-menu-button\" tabindex=\"-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span v-else class=\"block px-4 py-2 text-sm text-gray-300 cursor-not-allowed\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <!-- Mobile menu toggle, controls the 'mobileMenuOpen' state. -->\n <button type=\"button\" id=\"open-mobile-menu\" class=\"-ml-2 rounded-md p-2 pr-4 text-gray-400\">\n <span class=\"sr-only\">Open menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5\" />\n </svg>\n </button>\n </div>\n\n <!-- Mobile menu mask -->\n <div id=\"mobile-menu-mask\" class=\"fixed inset-0 bg-black bg-opacity-40 z-40 hidden\"></div>\n <!-- Mobile menu drawer -->\n <div id=\"mobile-menu\" class=\"fixed inset-0 bg-white shadow-lg z-50 transform translate-x-full transition-transform duration-200 ease-in-out flex flex-col\">\n <div class=\"flex items-center justify-between px-4 !h-[55px] border-b border-gray-200\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px]\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <button type=\"button\" id=\"close-mobile-menu\" class=\"text-gray-400 p-2 rounded-md\">\n <span class=\"sr-only\">Close menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <nav class=\"flex-1 px-4 py-4 space-y-2\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"documentView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Documents</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Documents\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"dashboardView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Dashboards</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat')\"\n href=\"#/chat\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"chatView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Chat</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <div v-if=\"!user && hasAPIKey\" class=\"mt-4\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"w-full rounded bg-ultramarine-600 px-3 py-2 text-base font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"mt-4\">\n <div class=\"flex items-center gap-3 px-3 py-2 bg-gray-50 rounded-md\">\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n <span class=\"text-gray-900 font-medium\">{{ user.name }}</span>\n </div>\n <div class=\"mt-2 space-y-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" class=\"block px-3 py-2 rounded-md text-base text-gray-700 hover:bg-ultramarine-100\">Team</router-link>\n <span v-else class=\"block px-3 py-2 rounded-md text-base text-gray-300 cursor-not-allowed\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span @click=\"logout\" class=\"block px-3 py-2 rounded-md text-base text-gray-700 hover:bg-ultramarine-100 cursor-pointer\">Sign out</span>\n </div>\n </div>\n </nav>\n </div>\n</div>\n";
|
|
5742
|
+
module.exports = "<div class=\"navbar w-full bg-gray-50 flex justify-between border-b border-gray-200 !h-[55px]\">\n <div class=\"flex items-center gap-4 h-full pl-4\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px] mr-1\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <div v-if=\"!!state.nodeEnv\" title=\"NODE_ENV\" class=\"inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900\" :class=\"warnEnv ? 'bg-red-300' : 'bg-yellow-300'\">\n {{state.nodeEnv}}\n </div>\n </div>\n <div class=\"h-full pr-4 hidden md:block\">\n <div class=\"sm:ml-6 sm:flex sm:space-x-8 h-full\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\"\n :class=\"documentView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Documents</a>\n <span v-else class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium text-gray-300 cursor-not-allowed\" aria-disabled=\"true\">\n Documents\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"dashboardView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Dashboards</a>\n <span v-else class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat')\"\n href=\"#/chat\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"chatView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Chat</a>\n <span v-else class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n\n <div class=\"h-full flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"h-full flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <div>\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800\" id=\"user-menu-button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <span class=\"absolute -inset-1.5\"></span>\n <span class=\"sr-only\">Open user menu</span>\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n </button>\n </div>\n\n <div v-if=\"showFlyout\" class=\"absolute right-0 z-[100] top-[90%] w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\" role=\"menu\" aria-orientation=\"vertical\" aria-labelledby=\"user-menu-button\" tabindex=\"-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span v-else class=\"block px-4 py-2 text-sm text-gray-300 cursor-not-allowed\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <!-- Mobile menu toggle, controls the 'mobileMenuOpen' state. -->\n <button type=\"button\" id=\"open-mobile-menu\" class=\"-ml-2 rounded-md p-2 pr-4 text-gray-400\">\n <span class=\"sr-only\">Open menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5\" />\n </svg>\n </button>\n </div>\n\n <!-- Mobile menu mask -->\n <div id=\"mobile-menu-mask\" class=\"fixed inset-0 bg-black bg-opacity-40 z-40 hidden\"></div>\n <!-- Mobile menu drawer -->\n <div id=\"mobile-menu\" class=\"fixed inset-0 bg-white shadow-lg z-50 transform translate-x-full transition-transform duration-200 ease-in-out flex flex-col\">\n <div class=\"flex items-center justify-between px-4 !h-[55px] border-b border-gray-200\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px]\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <button type=\"button\" id=\"close-mobile-menu\" class=\"text-gray-400 p-2 rounded-md\">\n <span class=\"sr-only\">Close menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <nav class=\"flex-1 px-4 py-4 space-y-2\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"documentView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Documents</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Documents\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"dashboardView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Dashboards</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat')\"\n href=\"#/chat\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"chatView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Chat</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <div v-if=\"!user && hasAPIKey\" class=\"mt-4\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"w-full rounded bg-ultramarine-600 px-3 py-2 text-base font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"mt-4\">\n <div class=\"flex items-center gap-3 px-3 py-2 bg-gray-50 rounded-md\">\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n <span class=\"text-gray-900 font-medium\">{{ user.name }}</span>\n </div>\n <div class=\"mt-2 space-y-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" class=\"block px-3 py-2 rounded-md text-base text-gray-700 hover:bg-ultramarine-100\">Team</router-link>\n <span v-else class=\"block px-3 py-2 rounded-md text-base text-gray-300 cursor-not-allowed\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span @click=\"logout\" class=\"block px-3 py-2 rounded-md text-base text-gray-700 hover:bg-ultramarine-100 cursor-pointer\">Sign out</span>\n </div>\n </div>\n </nav>\n </div>\n</div>\n";
|
|
5175
5743
|
|
|
5176
5744
|
/***/ }),
|
|
5177
5745
|
|
|
@@ -5204,7 +5772,7 @@ module.exports = "<div class=\"p-1\">\n <form class=\"space-y-4\">\n <div cl
|
|
|
5204
5772
|
/***/ ((module) => {
|
|
5205
5773
|
|
|
5206
5774
|
"use strict";
|
|
5207
|
-
module.exports = "<div class=\"mx-auto max-w-5xl py-6 px-2 flex flex-col gap-8\">\n <div>\n <div class=\"text-xl font-bold\">\n Subscription Details\n </div>\n <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-if=\"workspace && workspace.subscriptionTier\" class=\"mt-4 flex justify-between items-center\">\n <div>\n <span class=\"font-bold\">Tier:</span> {{workspace.subscriptionTier ?? 'No subscription'}}\n </div>\n <div>\n <async-button\n type=\"submit\"\n @click=\"getWorkspaceCustomerPortalLink\"\n class=\"inline-flex items-center justify-center rounded-md border border-transparent bg-ultramarine-600 py-1 px-2 text-sm font-medium text-white shadow-sm hover:bg-ultramarine-500 focus:outline-none focus:ring-2 focus:ring-forest-green-500 focus:ring-offset-2\">\n View in Stripe\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4 ml-1\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25\" />\n </svg>\n </async-button>\n </div>\n </div>\n <div v-else-if=\"workspace && !workspace.subscriptionTier\" class=\"mt-4 flex justify-between items-center\">\n <div>\n <span class=\"font-bold\">No active subscription</span>\n <div class=\"text-sm text-gray-700\">\n You won't be able to invite your team until you activate a subscription\n </div>\n </div>\n <div>\n <a\n :href=\"paymentLink\"\n target=\"_blank\"\n class=\"inline-flex items-center justify-center rounded-md border border-transparent bg-ultramarine-600 py-1 px-2 text-sm font-medium text-white shadow-sm hover:bg-ultramarine-500 focus:outline-none focus:ring-2 focus:ring-ultramarine-500 focus:ring-offset-2\">\n Subscribe With Stripe\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4 ml-1\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25\" />\n </svg>\n </a>\n </div>\n </div>\n </div>\n <div>\n <div class=\"text-xl font-bold\">\n Current Members\n </div>\n <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <ul v-else role=\"list\" class=\"divide-y divide-gray-100\">\n <li class=\"flex justify-between gap-x-6 py-5\" v-for=\"user in users\">\n <div class=\"flex min-w-0 gap-x-4\">\n <img class=\"size-12 flex-none rounded-full bg-gray-50\" :src=\"user.picture ?? 'images/logo.svg'\" alt=\"\">\n <div class=\"min-w-0 flex-auto\">\n <p class=\"text-sm/6 font-semibold text-gray-900\">\n {{user.name || user.githubUsername}}\n <span v-if=\"user.isFreeUser\" class=\"ml-1 inline-flex items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20\">Free</span>\n </p>\n <p class=\"mt-1 truncate text-xs/5 text-gray-500\">{{user.email ?? 'No Email'}}</p>\n </div>\n </div>\n <div class=\"hidden shrink-0 sm:flex sm:flex-col sm:items-end\">\n <p class=\"text-sm/6 text-gray-900 capitalize\">{{getRolesForUser(user).join(', ')}}</p>\n <div class=\"flex gap-3\">\n <p class=\"mt-1 text-xs/5 text-gray-500 cursor-pointer\">\n Edit\n </p>\n <button\n class=\"mt-1 text-xs/5 text-valencia-500 cursor-pointer disabled:cursor-not-allowed disabled:text-gray-300\"\n :disabled=\"getRolesForUser(user).includes('owner')\"\n @click=\"showRemoveModal = user\">\n Remove\n </button>\n </div>\n </div>\n </li>\n </ul>\n </div>\n <div>\n <div class=\"flex items-center justify-between\">\n <div class=\"text-xl font-bold\">\n Invitations\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showNewInvitationModal = true\"\n :disabled=\"status === 'loading'\"\n :tier=\"workspace?.subscriptionTier\"\n class=\"block rounded-md bg-ultramarine-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 disabled:bg-gray-500 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n New Invitation\n <svg class=\"inline w-4 h-4 ml-1\" v-if=\"workspace && !workspace.subscriptionTier\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M12 1.5a5.25 5.25 0 00-5.25 5.25v3a3 3 0 00-3 3v6.75a3 3 0 003 3h10.5a3 3 0 003-3v-6.75a3 3 0 00-3-3v-3c0-2.9-2.35-5.25-5.25-5.25zm3.75 8.25v-3a3.75 3.75 0 10-7.5 0v3h7.5z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-if=\"invitations?.length > 0\" class=\"mt-8 flow-root\" v-if=\"invitations?.length > 0\">\n <div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\">\n <div class=\"inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8\">\n <table class=\"min-w-full divide-y divide-gray-300\">\n <thead>\n <tr>\n <th scope=\"col\" class=\"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0\">GitHub Username</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Email</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Status</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Role</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"invitation in invitations\">\n <td class=\"whitespace-nowrap py-5 pl-4 pr-3 text-sm sm:pl-0\">\n {{invitation.githubUsername}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.email}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n <span class=\"inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-600/20\">\n Pending\n </span>\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.roles.join(', ')}}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n <div v-else-if=\"invitations?.length === 0\" class=\"mt-4\">\n <div class=\"text-center\">\n <svg class=\"mx-auto size-12 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path vector-effect=\"non-scaling-stroke\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z\" />\n </svg>\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No invitations</h3>\n <p class=\"mt-1 text-sm text-gray-500\">You have no outstanding invitations</p>\n </div>\n </div>\n </div>\n\n <modal v-if=\"showNewInvitationModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showNewInvitationModal = false\">×</div>\n <new-invitation @close=\"showNewInvitationModal = false\" @invitationCreated=\"invitations.push($event.invitation)\"></new-invitation>\n </template>\n </modal>\n\n <modal v-if=\"showRemoveModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showRemoveModal = false\">×</div>\n <div>\n Are you sure you want to remove user <span class=\"font-bold\">{{showRemoveModal.githubUsername}}</span> from this workspace?\n </div>\n <div class=\"mt-6 grid grid-cols-2 gap-4\">\n <async-button\n @click=\"removeFromWorkspace(showConfirmDeleteModal)\"\n class=\"border-0 mt-0 flex w-full items-center justify-center gap-3 rounded-md bg-valencia-500 hover:bg-valencia-400 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-400\">\n <span class=\"text-sm font-semibold leading-6\">Yes, Remove</span>\n </async-button>\n\n <span @click=\"showRemoveModal = null\" class=\"cursor-pointer flex w-full items-center justify-center gap-3 rounded-md bg-slate-500 hover:bg-slate-400 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-400\">\n <span class=\"text-sm font-semibold leading-6\">Cancel</span>\n </span>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
5775
|
+
module.exports = "<div class=\"mx-auto max-w-5xl py-6 px-2 flex flex-col gap-8\">\n <div>\n <div class=\"text-xl font-bold\">\n Subscription Details\n </div>\n <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-if=\"workspace && workspace.subscriptionTier\" class=\"mt-4 flex justify-between items-center\">\n <div>\n <span class=\"font-bold\">Tier:</span> {{workspace.subscriptionTier ?? 'No subscription'}}\n </div>\n <div>\n <async-button\n type=\"submit\"\n @click=\"getWorkspaceCustomerPortalLink\"\n class=\"inline-flex items-center justify-center rounded-md border border-transparent bg-ultramarine-600 py-1 px-2 text-sm font-medium text-white shadow-sm hover:bg-ultramarine-500 focus:outline-none focus:ring-2 focus:ring-forest-green-500 focus:ring-offset-2\">\n View in Stripe\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4 ml-1\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25\" />\n </svg>\n </async-button>\n </div>\n </div>\n <div v-else-if=\"workspace && !workspace.subscriptionTier\" class=\"mt-4 flex justify-between items-center\">\n <div>\n <span class=\"font-bold\">No active subscription</span>\n <div class=\"text-sm text-gray-700\">\n You won't be able to invite your team until you activate a subscription\n </div>\n </div>\n <div>\n <a\n :href=\"paymentLink\"\n target=\"_blank\"\n class=\"inline-flex items-center justify-center rounded-md border border-transparent bg-ultramarine-600 py-1 px-2 text-sm font-medium text-white shadow-sm hover:bg-ultramarine-500 focus:outline-none focus:ring-2 focus:ring-ultramarine-500 focus:ring-offset-2\">\n Subscribe With Stripe\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4 ml-1\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25\" />\n </svg>\n </a>\n </div>\n </div>\n </div>\n <div>\n <div class=\"text-xl font-bold\">\n Current Members\n </div>\n <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <ul v-else role=\"list\" class=\"divide-y divide-gray-100\">\n <li class=\"flex justify-between gap-x-6 py-5\" v-for=\"user in users\">\n <div class=\"flex min-w-0 gap-x-4\">\n <img class=\"size-12 flex-none rounded-full bg-gray-50\" :src=\"user.picture ?? 'images/logo.svg'\" alt=\"\">\n <div class=\"min-w-0 flex-auto\">\n <p class=\"text-sm/6 font-semibold text-gray-900\">\n {{user.name || user.githubUsername}}\n <span v-if=\"user.isFreeUser\" class=\"ml-1 inline-flex items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20\">Free</span>\n </p>\n <p class=\"mt-1 truncate text-xs/5 text-gray-500\">{{user.email ?? 'No Email'}}</p>\n </div>\n </div>\n <div class=\"hidden shrink-0 sm:flex sm:flex-col sm:items-end\">\n <p class=\"text-sm/6 text-gray-900 capitalize\">{{getRolesForUser(user).join(', ')}}</p>\n <div class=\"flex gap-3\">\n <button\n type=\"button\"\n class=\"mt-1 text-xs/5 text-gray-500 cursor-pointer disabled:cursor-not-allowed disabled:text-gray-300\"\n :disabled=\"getRolesForUser(user).includes('owner')\"\n @click=\"openEditModal(user)\">\n Edit\n </button>\n <button\n class=\"mt-1 text-xs/5 text-valencia-500 cursor-pointer disabled:cursor-not-allowed disabled:text-gray-300\"\n :disabled=\"getRolesForUser(user).includes('owner')\"\n @click=\"showRemoveModal = user\">\n Remove\n </button>\n </div>\n </div>\n </li>\n </ul>\n </div>\n <div>\n <div class=\"flex items-center justify-between\">\n <div class=\"text-xl font-bold\">\n Invitations\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showNewInvitationModal = true\"\n :disabled=\"status === 'loading'\"\n :tier=\"workspace?.subscriptionTier\"\n class=\"block rounded-md bg-ultramarine-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 disabled:bg-gray-500 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n New Invitation\n <svg class=\"inline w-4 h-4 ml-1\" v-if=\"workspace && !workspace.subscriptionTier\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M12 1.5a5.25 5.25 0 00-5.25 5.25v3a3 3 0 00-3 3v6.75a3 3 0 003 3h10.5a3 3 0 003-3v-6.75a3 3 0 00-3-3v-3c0-2.9-2.35-5.25-5.25-5.25zm3.75 8.25v-3a3.75 3.75 0 10-7.5 0v3h7.5z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-if=\"invitations?.length > 0\" class=\"mt-8 flow-root\" v-if=\"invitations?.length > 0\">\n <div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\">\n <div class=\"inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8\">\n <table class=\"min-w-full divide-y divide-gray-300\">\n <thead>\n <tr>\n <th scope=\"col\" class=\"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0\">GitHub Username</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Email</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Status</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Role</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"invitation in invitations\">\n <td class=\"whitespace-nowrap py-5 pl-4 pr-3 text-sm sm:pl-0\">\n {{invitation.githubUsername}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.email}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n <span class=\"inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-600/20\">\n Pending\n </span>\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.roles.join(', ')}}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n <div v-else-if=\"invitations?.length === 0\" class=\"mt-4\">\n <div class=\"text-center\">\n <svg class=\"mx-auto size-12 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path vector-effect=\"non-scaling-stroke\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z\" />\n </svg>\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No invitations</h3>\n <p class=\"mt-1 text-sm text-gray-500\">You have no outstanding invitations</p>\n </div>\n </div>\n </div>\n\n <modal v-if=\"showNewInvitationModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showNewInvitationModal = false\">×</div>\n <new-invitation @close=\"showNewInvitationModal = false\" @invitationCreated=\"invitations.push($event.invitation)\"></new-invitation>\n </template>\n </modal>\n\n <modal v-if=\"showEditModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"closeEditModal\">×</div>\n <div class=\"p-1 space-y-4\">\n <div class=\"text-lg font-bold\">\n Edit Member\n </div>\n\n <div>\n <div class=\"text-sm/6 font-semibold text-gray-900\">\n {{showEditModal.user.name || showEditModal.user.githubUsername}}\n </div>\n <div class=\"text-xs/5 text-gray-500\">\n {{showEditModal.user.email ?? 'No Email'}}\n </div>\n </div>\n\n <div>\n <label for=\"editRole\" class=\"block text-sm/6 font-medium text-gray-900\">Role</label>\n <div class=\"mt-2 grid grid-cols-1\">\n <select\n id=\"editRole\"\n name=\"editRole\"\n v-model=\"showEditModal.role\"\n class=\"col-start-1 row-start-1 w-full appearance-none rounded-md bg-white py-1.5 pl-3 pr-8 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-ultramarine-600 sm:text-sm/6\">\n <option value=\"admin\" :disabled=\"disableRoleOption('admin')\">Admin</option>\n <option value=\"member\" :disabled=\"disableRoleOption('member')\">Member</option>\n <option value=\"readonly\" :disabled=\"disableRoleOption('readonly')\">Read-only</option>\n <option value=\"dashboards\" :disabled=\"disableRoleOption('dashboards')\">Dashboards Only</option>\n </select>\n <svg class=\"pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-gray-500 sm:size-4\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\" data-slot=\"icon\">\n <path fill-rule=\"evenodd\" d=\"M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div v-if=\"!workspace?.subscriptionTier\" class=\"mt-2 text-sm text-gray-700\">\n You can only assign the \"Dashboards Only\" role until you activate a subscription.\n </div>\n </div>\n\n <div class=\"mt-6 grid grid-cols-2 gap-4\">\n <async-button\n @click=\"updateWorkspaceMember\"\n :disabled=\"showEditModal.role === showEditModal.originalRole\"\n class=\"border-0 mt-0 flex w-full items-center justify-center gap-3 rounded-md bg-ultramarine-600 hover:bg-ultramarine-500 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-500\">\n <span class=\"text-sm font-semibold leading-6\">Save</span>\n </async-button>\n\n <span @click=\"closeEditModal\" class=\"cursor-pointer flex w-full items-center justify-center gap-3 rounded-md bg-slate-500 hover:bg-slate-400 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-400\">\n <span class=\"text-sm font-semibold leading-6\">Cancel</span>\n </span>\n </div>\n </div>\n </template>\n </modal>\n\n <modal v-if=\"showRemoveModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showRemoveModal = null\">×</div>\n <div>\n Are you sure you want to remove user <span class=\"font-bold\">{{showRemoveModal.githubUsername}}</span> from this workspace?\n </div>\n <div class=\"mt-6 grid grid-cols-2 gap-4\">\n <async-button\n @click=\"removeFromWorkspace\"\n class=\"border-0 mt-0 flex w-full items-center justify-center gap-3 rounded-md bg-valencia-500 hover:bg-valencia-400 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-400\">\n <span class=\"text-sm font-semibold leading-6\">Yes, Remove</span>\n </async-button>\n\n <span @click=\"showRemoveModal = null\" class=\"cursor-pointer flex w-full items-center justify-center gap-3 rounded-md bg-slate-500 hover:bg-slate-400 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-400\">\n <span class=\"text-sm font-semibold leading-6\">Cancel</span>\n </span>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
5208
5776
|
|
|
5209
5777
|
/***/ }),
|
|
5210
5778
|
|
|
@@ -15267,7 +15835,7 @@ var bson = /*#__PURE__*/Object.freeze({
|
|
|
15267
15835
|
/***/ ((module) => {
|
|
15268
15836
|
|
|
15269
15837
|
"use strict";
|
|
15270
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.
|
|
15838
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.129","description":"A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.","homepage":"https://studio.mongoosejs.io/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"dependencies":{"archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.1.0","marked":"15.0.12","node-inspect-extracted":"3.x","tailwindcss":"3.4.0","vanillatoasts":"^1.6.0","vue":"3.x","webpack":"5.x"},"peerDependencies":{"bson":"^5.5.1 || 6.x","express":"4.x","mongoose":"7.x || 8.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongoose":"8.x"},"scripts":{"lint":"eslint .","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js"}}');
|
|
15271
15839
|
|
|
15272
15840
|
/***/ })
|
|
15273
15841
|
|