diff --git a/package.json b/package.json
index e86d7cd62..1c4e07098 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,12 @@
},
"dependencies": {
"@chialab/typos": "^0.1.1",
+ "@fullcalendar/core": "^6.1.15",
+ "@fullcalendar/daygrid": "^6.1.15",
+ "@fullcalendar/interaction": "^6.1.15",
+ "@fullcalendar/list": "^6.1.15",
+ "@fullcalendar/timegrid": "^6.1.15",
+ "@fullcalendar/vue": "^6.1.15",
"@riophae/vue-treeselect": "^0.4.0",
"@trevoreyre/autocomplete-vue": "^2.4.1",
"abortcontroller-polyfill": "^1.7.6",
@@ -76,4 +82,4 @@
"webpack-watch-files-plugin": "^1.2.0"
},
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
-}
\ No newline at end of file
+}
diff --git a/resources/js/app/app.js b/resources/js/app/app.js
index 746b02120..81596305e 100644
--- a/resources/js/app/app.js
+++ b/resources/js/app/app.js
@@ -110,6 +110,7 @@ const _vueInstance = new Vue({
FieldString: () => import(/* webpackChunkName: "field-string" */'app/components/form/field-string'),
FieldTextarea: () => import(/* webpackChunkName: "field-textarea" */'app/components/form/field-textarea'),
FieldTitle: () => import(/* webpackChunkName: "field-title" */'app/components/form/field-title'),
+ CalendarView: () => import(/* webpackChunkName: "calendar-view" */'app/components/calendar-view/calendar-view'),
ObjectInfo: () => import(/* webpackChunkName: "object-info" */'app/components/object-info/object-info'),
RelatedObjectsFilter: () => import(/* webpackChunkName: "related-objects-filter" */'app/components/related-objects-filter/related-objects-filter'),
ModuleProperties: () => import(/* webpackChunkName: "module-properties" */'app/components/module/module-properties'),
@@ -625,6 +626,7 @@ window._vueInstance = _vueInstance;
// use component everywhere in Manager
Vue.component('AppIcon', AppIcon);
+Vue.component('CalendarView', _vueInstance.$options.components.CalendarView);
Vue.component('ClipboardItem', _vueInstance.$options.components.ClipboardItem);
Vue.component('DateRangesView', _vueInstance.$options.components.DateRangesView);
Vue.component('FieldCheckbox', _vueInstance.$options.components.FieldCheckbox);
diff --git a/resources/js/app/components/calendar-view/calendar-view.vue b/resources/js/app/components/calendar-view/calendar-view.vue
new file mode 100644
index 000000000..2f70c724f
--- /dev/null
+++ b/resources/js/app/components/calendar-view/calendar-view.vue
@@ -0,0 +1,533 @@
+
+
+
+
+
+
+
+
+ {{ msgLoading }} ...
+
+
+
+
+
+
+ {{ ftime(arg.event.start) }} - {{ ftime(arg.event.end) }}
+
+
+
+
+ {{ arg.event.title }}
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/app/components/date-range/date-range.vue b/resources/js/app/components/date-range/date-range.vue
index 4f8db81bb..84ebdbf18 100644
--- a/resources/js/app/components/date-range/date-range.vue
+++ b/resources/js/app/components/date-range/date-range.vue
@@ -7,6 +7,7 @@
{{ msgFrom }}
diff --git a/resources/js/app/components/object-info/object-info.vue b/resources/js/app/components/object-info/object-info.vue
index eb0ce58fa..42e7a38f4 100644
--- a/resources/js/app/components/object-info/object-info.vue
+++ b/resources/js/app/components/object-info/object-info.vue
@@ -6,6 +6,7 @@
@@ -17,6 +18,14 @@ import { t } from 'ttag';
export default {
name: 'ObjectInfo',
props: {
+ borderColor: {
+ type: String,
+ default: 'black',
+ },
+ color: {
+ type: String,
+ default: 'white',
+ },
objectData: {
type: Object,
required: true
@@ -28,6 +37,10 @@ export default {
fields: ['title', 'description'],
labelsMap: new Map(),
msgShowObjectInfo: t`Show object info`,
+ styles: {
+ borderColor: this.borderColor,
+ color: this.color,
+ },
reloadedData: this.objectData || {},
values: {},
};
diff --git a/resources/js/app/helpers/view.js b/resources/js/app/helpers/view.js
index 4ffc7797d..adb360550 100644
--- a/resources/js/app/helpers/view.js
+++ b/resources/js/app/helpers/view.js
@@ -348,6 +348,12 @@ export default {
return d ? new Date(d).toLocaleDateString(locale) + ' ' + new Date(d).toLocaleTimeString(locale, {hour: '2-digit', minute:'2-digit'}) : '';
},
+ formatTime(d) {
+ const locale = BEDITA?.locale?.slice(0, 2) || 'en';
+
+ return d ? new Date(d).toLocaleTimeString(locale, {hour: '2-digit', minute:'2-digit'}) : '';
+ },
+
formatBytes(size) {
let i = size == 0 ? 0 : Math.floor( Math.log(size) / Math.log(1024) );
diff --git a/resources/js/app/pages/modules/index.js b/resources/js/app/pages/modules/index.js
index b91262f50..36dff240b 100644
--- a/resources/js/app/pages/modules/index.js
+++ b/resources/js/app/pages/modules/index.js
@@ -20,6 +20,7 @@ export default {
IndexCell: () => import(/* webpackChunkName: "index-cell" */'app/components/index-cell/index-cell'),
PermissionToggle: () => import(/* webpackChunkName: "permission-toggle" */'app/components/permission-toggle/permission-toggle'),
DropUpload: () => import(/* webpackChunkName: "drop-upload" */'app/components/drop-upload'),
+ CalendarView: () => import(/* webpackChunkName: "calendar-view" */'app/components/calendar-view/calendar-view'),
},
/**
diff --git a/resources/styles/_elements.scss b/resources/styles/_elements.scss
index 5f5e097cf..c0744b616 100644
--- a/resources/styles/_elements.scss
+++ b/resources/styles/_elements.scss
@@ -350,3 +350,54 @@ nav.pagination {
right: 6px;
font-size: 11px;
}
+
+// fullcalendar
+.fc {
+ .fc-list-day-cushion {
+ background-color: #154362 !important;
+ }
+ .fc-list-event:hover td {
+ background-color: #5389ae !important;
+ }
+}
+:root {
+ --fc-small-font-size: .75em;
+ --fc-highlight-color: #bcd6f14d;
+ --fc-today-bg-color: #002732;
+ --fc-event-bg-color: #4871ed;
+}
+.fc-toolbar-chunk .fc-datePicker-button input[type="month"],
+.fc-toolbar-chunk .fc-datePicker-button input[type="date"] {
+ background: #fff !important;
+ color: #222 !important;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: none !important;
+ padding: 2px 6px;
+ font-size: 1em;
+ outline: none;
+ margin: 0 4px;
+ min-width: 120px;
+ height: 2em;
+}
+.fc-toolbar-chunk .fc-datePicker-button input[type="month"]:focus,
+.fc-toolbar-chunk .fc-datePicker-button input[type="date"]:focus {
+ border-color: #888;
+}
+.fc-toolbar-chunk .fc-datePicker-button {
+ background: transparent !important;
+ box-shadow: none !important;
+ padding: 0 !important;
+}
+.fc-toolbar-chunk .fc-datePicker-button,
+.fc-toolbar-chunk .fc-datePicker-button.fc-button {
+ background: transparent !important;
+ border: none !important;
+ box-shadow: none !important;
+ padding: 0 !important;
+ min-width: 0 !important;
+ height: auto !important;
+ display: flex;
+ align-items: center;
+ cursor: default;
+}
diff --git a/src/View/Helper/LayoutHelper.php b/src/View/Helper/LayoutHelper.php
index 44c89e7c3..c8244d596 100644
--- a/src/View/Helper/LayoutHelper.php
+++ b/src/View/Helper/LayoutHelper.php
@@ -26,6 +26,7 @@
* @property \Cake\View\Helper\HtmlHelper $Html
* @property \App\View\Helper\LinkHelper $Link
* @property \App\View\Helper\PermsHelper $Perms
+ * @property \App\View\Helper\PropertyHelper $Property
* @property \App\View\Helper\SystemHelper $System
* @property \Cake\View\Helper\UrlHelper $Url
*/
@@ -36,7 +37,7 @@ class LayoutHelper extends Helper
*
* @var array
*/
- public $helpers = ['Editors', 'Html', 'Link', 'Perms', 'System', 'Url'];
+ public $helpers = ['Editors', 'Html', 'Link', 'Perms', 'Property', 'System', 'Url'];
/**
* Is Dashboard
@@ -278,8 +279,11 @@ public function moduleIndexDefaultViewType(): string
{
$module = (array)$this->getView()->get('currentModule');
$name = (string)Hash::get($module, 'name');
+ $schema = (array)$this->_View->get('schema');
+ $defaultType = in_array('DateRanges', (array)Hash::get($schema, 'associations')) ? 'calendar' : 'list';
+ $defaultType = $name === 'folders' ? 'tree' : $defaultType;
- return $name === 'folders' ? 'tree' : 'list';
+ return $defaultType;
}
/**
@@ -302,8 +306,10 @@ public function moduleIndexViewType(): string
public function moduleIndexViewTypes(): array
{
$defaultType = $this->moduleIndexDefaultViewType();
+ $defaultList = $defaultType === 'calendar' ? ['calendar', 'list'] : ['list'];
+ $defaultList = $defaultType === 'tree' ? ['tree', 'list'] : $defaultList;
- return $defaultType === 'tree' ? ['tree', 'list'] : ['list'];
+ return $defaultList;
}
/**
@@ -377,6 +383,7 @@ public function metaConfig(): array
'richeditorConfig' => (array)Configure::read('Richeditor'),
'richeditorByPropertyConfig' => $this->uiRicheditorConfig(),
'indexLists' => (array)$this->indexLists(),
+ 'fastCreateFields' => (array)$this->Property->fastCreateFieldsMap(),
];
}
diff --git a/templates/Element/Modules/calendar.twig b/templates/Element/Modules/calendar.twig
new file mode 100644
index 000000000..83e65301e
--- /dev/null
+++ b/templates/Element/Modules/calendar.twig
@@ -0,0 +1,5 @@
+
diff --git a/templates/Element/Modules/sidebar.twig b/templates/Element/Modules/sidebar.twig
index 4b0ff0223..5b6dfe426 100644
--- a/templates/Element/Modules/sidebar.twig
+++ b/templates/Element/Modules/sidebar.twig
@@ -38,17 +38,27 @@
{% set indexViewTypes = Layout.moduleIndexViewTypes() %}
{% if indexViewTypes|length > 1 %}
- {% set indexViewType = Layout.moduleIndexViewType() %}
- {% for t in indexViewTypes %}
- {% if t != indexViewType %}
- {% set icon = t == 'tree' ? 'carbon:tree-view' : 'carbon:list' %}
- {% set label = t == 'tree' ? __('Tree view') : __('List view') %}
- {% do _view.append(
+ {% set mapIcon = {
+ 'list': 'carbon:list',
+ 'tree': 'carbon:tree-view',
+ 'calendar': 'carbon:calendar',
+ } %}
+ {% set mapLabel = {
+ 'list': __('List'),
+ 'tree': __('Tree'),
+ 'calendar': __('Calendar'),
+ } %}
+ {% set indexViewType = Layout.moduleIndexViewType() %}
+ {% for t in indexViewTypes %}
+ {% if t != indexViewType %}
+ {% set icon = mapIcon[t] %}
+ {% set label = mapLabel[t] %}
+ {% do _view.append(
'app-module-buttons',
- '' ~ __(label) ~ ''
+ '' ~ label ~ ''
) %}
- {% endif %}
- {% endfor %}
+ {% endif %}
+ {% endfor %}
{% endif %}
{% if in_array('admin', user.roles) %}
diff --git a/templates/Pages/Modules/index.twig b/templates/Pages/Modules/index.twig
index b525f6927..dae94107a 100644
--- a/templates/Pages/Modules/index.twig
+++ b/templates/Pages/Modules/index.twig
@@ -1,7 +1,13 @@
{% do _view.assign('title', __(currentModule.name|humanize)) %}
{% set indexViewType = Layout.moduleIndexViewType() %}
-
-{{ element('Modules/index_header', { 'meta': meta, 'filter': filter, 'Schema': Schema, 'hidePagination': indexViewType == 'tree'}) }}
+{% if indexViewType != 'calendar' %}
+ {{ element('Modules/index_header', {
+ 'meta': meta,
+ 'filter': filter,
+ 'Schema': Schema,
+ 'hidePagination': indexViewType == 'tree'}
+ ) }}
+{% endif %}
{% set ids = Array.extract(objects, '{*}.id') %}
diff --git a/tests/TestCase/View/Helper/LayoutHelperTest.php b/tests/TestCase/View/Helper/LayoutHelperTest.php
index 16abb2e33..dbb311d58 100644
--- a/tests/TestCase/View/Helper/LayoutHelperTest.php
+++ b/tests/TestCase/View/Helper/LayoutHelperTest.php
@@ -17,6 +17,7 @@
use App\View\Helper\EditorsHelper;
use App\View\Helper\LayoutHelper;
use App\View\Helper\PermsHelper;
+use App\View\Helper\PropertyHelper;
use App\View\Helper\SystemHelper;
use Cake\Cache\Cache;
use Cake\Core\Configure;
@@ -579,6 +580,7 @@ public function testMetaConfig(): void
$view = new View($request, null, null, compact('viewVars'));
$layout = new LayoutHelper($view);
$system = new SystemHelper($view);
+ $property = new PropertyHelper($view);
$conf = $layout->metaConfig();
$expected = [
'base' => '',
@@ -599,6 +601,7 @@ public function testMetaConfig(): void
'richeditorConfig' => (array)Configure::read('Richeditor'),
'richeditorByPropertyConfig' => $layout->uiRicheditorConfig(),
'indexLists' => (array)$layout->indexLists(),
+ 'fastCreateFields' => (array)$property->fastCreateFieldsMap(),
];
static::assertSame($expected, $conf);
Cache::disable();
diff --git a/yarn.lock b/yarn.lock
index 3b97be744..98dcafa4c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1014,6 +1014,40 @@
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
+"@fullcalendar/core@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.15.tgz#6c3f5259fc4589870228853072131219bb533f6e"
+ integrity sha512-BuX7o6ALpLb84cMw1FCB9/cSgF4JbVO894cjJZ6kP74jzbUZNjtwffwRdA+Id8rrLjT30d/7TrkW90k4zbXB5Q==
+ dependencies:
+ preact "~10.12.1"
+
+"@fullcalendar/daygrid@^6.1.15", "@fullcalendar/daygrid@~6.1.15":
+ version "6.1.15"
+ resolved "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.15.tgz#91208b0955ba805ddad285a53ee6f53855146963"
+ integrity sha512-j8tL0HhfiVsdtOCLfzK2J0RtSkiad3BYYemwQKq512cx6btz6ZZ2RNc/hVnIxluuWFyvx5sXZwoeTJsFSFTEFA==
+
+"@fullcalendar/interaction@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.15.tgz#1c685d5c269388d4877b75ab2185e97d7c386cc7"
+ integrity sha512-DOTSkofizM7QItjgu7W68TvKKvN9PSEEvDJceyMbQDvlXHa7pm/WAVtAc6xSDZ9xmB1QramYoWGLHkCYbTW1rQ==
+
+"@fullcalendar/list@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.15.tgz#d9b7ff0a50d7efa0d31234ed6caea06db6090c29"
+ integrity sha512-U1bce04tYDwkFnuVImJSy2XalYIIQr6YusOWRPM/5ivHcJh67Gm8CIMSWpi3KdRSNKFkqBxLPkfZGBMaOcJYug==
+
+"@fullcalendar/timegrid@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.15.tgz#c4630b7c03c813065154c6e3981f8d51d9d692e5"
+ integrity sha512-61ORr3A148RtxQ2FNG7JKvacyA/TEVZ7z6I+3E9Oeu3dqTf6M928bFcpehRTIK6zIA6Yifs7BeWHgOE9dFnpbw==
+ dependencies:
+ "@fullcalendar/daygrid" "~6.1.15"
+
+"@fullcalendar/vue@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.npmjs.org/@fullcalendar/vue/-/vue-6.1.15.tgz#3f8069f63769ebb0eb0fa9f3696f226223965781"
+ integrity sha512-ptTyhJMwY0kgUQSQtR7Nh8AMdUOdEYUmQu7aU604m776glDh80U6bfm/xLdZKG7EmlAbFAtXnymOUSSxnsGhEQ==
+
"@humanwhocodes/config-array@^0.13.0":
version "0.13.0"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748"
@@ -4560,6 +4594,11 @@ potpack@^2.0.0:
resolved "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz#61f4dd2dc4b3d5e996e3698c0ec9426d0e169104"
integrity sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==
+preact@~10.12.1:
+ version "10.12.1"
+ resolved "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz#8f9cb5442f560e532729b7d23d42fd1161354a21"
+ integrity sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==
+
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"