mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 01:32:38 +00:00
Implement custom entities / objects (closes #242)
This commit is contained in:
parent
918f84f568
commit
096fb7a116
@ -1,3 +1,8 @@
|
||||
### New feature: Custom entities / objects
|
||||
- Custom entities are based on Userfields and can be used to add any custom lists you want to have in grocy
|
||||
- They can have an own menu entry in the sidebar
|
||||
- => See "Manage master data" -> Userentities or try it on the demo: https://demo.grocy.info/userobjects/exampleuserentity
|
||||
|
||||
### Stock improvements
|
||||
- Products can now have variations (nested products)
|
||||
- Define the parent product for a product on the product edit page (only one level is possible, means a product which is used as a parent product in another product, cannot have a parent product itself)
|
||||
|
@ -16,21 +16,10 @@ class BaseController
|
||||
$localizationService = new LocalizationService(GROCY_CULTURE);
|
||||
$this->LocalizationService = $localizationService;
|
||||
|
||||
if (GROCY_MODE === 'prerelease')
|
||||
{
|
||||
$commitHash = trim(exec('git log --pretty="%h" -n1 HEAD'));
|
||||
$commitDate = trim(exec('git log --date=iso --pretty="%cd" -n1 HEAD'));
|
||||
|
||||
$container->view->set('version', "pre-release-$commitHash");
|
||||
$container->view->set('releaseDate', \substr($commitDate, 0, 19));
|
||||
}
|
||||
else
|
||||
{
|
||||
$applicationService = new ApplicationService();
|
||||
$versionInfo = $applicationService->GetInstalledVersion();
|
||||
$container->view->set('version', $versionInfo->Version);
|
||||
$container->view->set('releaseDate', $versionInfo->ReleaseDate);
|
||||
}
|
||||
$applicationService = new ApplicationService();
|
||||
$versionInfo = $applicationService->GetInstalledVersion();
|
||||
$container->view->set('version', $versionInfo->Version);
|
||||
$container->view->set('releaseDate', $versionInfo->ReleaseDate);
|
||||
|
||||
$container->view->set('__t', function(string $text, ...$placeholderValues) use($localizationService)
|
||||
{
|
||||
@ -64,6 +53,8 @@ class BaseController
|
||||
}
|
||||
$container->view->set('featureFlags', $constants);
|
||||
|
||||
$container->view->set('userentitiesForSidebar', $this->Database->userentities()->where('show_in_sidebar_menu = 1')->orderBy('name'));
|
||||
|
||||
try
|
||||
{
|
||||
$usersService = new UsersService();
|
||||
|
@ -22,6 +22,25 @@ class GenericEntityController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function UserentitiesList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'userentities', [
|
||||
'userentities' => $this->Database->userentities()->orderBy('name')
|
||||
]);
|
||||
}
|
||||
|
||||
public function UserobjectsList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
$userentity = $this->Database->userentities()->where('name = :1', $args['userentityName'])->fetch();
|
||||
|
||||
return $this->AppContainer->view->render($response, 'userobjects', [
|
||||
'userentity' => $userentity,
|
||||
'userobjects' => $this->Database->userobjects()->where('userentity_id = :1', $userentity->id),
|
||||
'userfields' => $this->UserfieldsService->GetFields('userentity-' . $args['userentityName']),
|
||||
'userfieldValues' => $this->UserfieldsService->GetAllValues('userentity-' . $args['userentityName'])
|
||||
]);
|
||||
}
|
||||
|
||||
public function UserfieldEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
if ($args['userfieldId'] == 'new')
|
||||
@ -42,4 +61,44 @@ class GenericEntityController extends BaseController
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function UserentityEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
if ($args['userentityId'] == 'new')
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'userentityform', [
|
||||
'mode' => 'create'
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'userentityform', [
|
||||
'mode' => 'edit',
|
||||
'userentity' => $this->Database->userentities($args['userentityId'])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function UserobjectEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
$userentity = $this->Database->userentities()->where('name = :1', $args['userentityName'])->fetch();
|
||||
|
||||
if ($args['userobjectId'] == 'new')
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'userobjectform', [
|
||||
'userentity' => $userentity,
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->UserfieldsService->GetFields('userentity-' . $args['userentityName'])
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'userobjectform', [
|
||||
'userentity' => $userentity,
|
||||
'mode' => 'edit',
|
||||
'userobject' => $this->Database->userobjects($args['userobjectId']),
|
||||
'userfields' => $this->UserfieldsService->GetFields('userentity-' . $args['userentityName'])
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2515,6 +2515,8 @@
|
||||
"equipment",
|
||||
"api_keys",
|
||||
"userfields",
|
||||
"userentities",
|
||||
"userobjects",
|
||||
"meal_plan"
|
||||
]
|
||||
},
|
||||
|
@ -288,3 +288,15 @@ msgid "Slice"
|
||||
msgid_plural "Slices"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Example userentity"
|
||||
msgstr ""
|
||||
|
||||
msgid "This is an example user entity..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Custom field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Example field value..."
|
||||
msgstr ""
|
||||
|
@ -1420,3 +1420,33 @@ msgstr ""
|
||||
|
||||
msgid "Consume product on chore execution"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure to delete user field \"%s\"?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Userentities"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create userentity"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show in sidebar menu"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit userentity"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure to delete this userobject?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Icon CSS class"
|
||||
msgstr ""
|
||||
|
||||
msgid "For example"
|
||||
msgstr ""
|
||||
|
17
migrations/0085.sql
Normal file
17
migrations/0085.sql
Normal file
@ -0,0 +1,17 @@
|
||||
CREATE TABLE userentities (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
caption TEXT NOT NULL,
|
||||
description TEXT,
|
||||
show_in_sidebar_menu TINYINT NOT NULL DEFAULT 1,
|
||||
icon_css_class TEXT,
|
||||
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime')),
|
||||
|
||||
UNIQUE(name)
|
||||
);
|
||||
|
||||
CREATE TABLE userobjects (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
userentity_id INTEGER NOT NULL,
|
||||
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
);
|
69
public/viewjs/userentities.js
Normal file
69
public/viewjs/userentities.js
Normal file
@ -0,0 +1,69 @@
|
||||
var userentitiesTable = $('#userentities-table').DataTable({
|
||||
'paginate': false,
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 }
|
||||
],
|
||||
'language': JSON.parse(__t('datatables_localization')),
|
||||
'scrollY': false,
|
||||
'colReorder': true,
|
||||
'stateSave': true,
|
||||
'stateSaveParams': function(settings, data)
|
||||
{
|
||||
data.search.search = "";
|
||||
|
||||
data.columns.forEach(column =>
|
||||
{
|
||||
column.search.search = "";
|
||||
});
|
||||
}
|
||||
});
|
||||
$('#userentities-table tbody').removeClass("d-none");
|
||||
userentitiesTable.columns.adjust().draw();
|
||||
|
||||
$("#search").on("keyup", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
if (value === "all")
|
||||
{
|
||||
value = "";
|
||||
}
|
||||
|
||||
userentitiesTable.search(value).draw();
|
||||
});
|
||||
|
||||
$(document).on('click', '.userentity-delete-button', function (e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-userentity-name');
|
||||
var objectId = $(e.currentTarget).attr('data-userentity-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: __t('Are you sure to delete userentity "%s"?', objectName),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: __t('Yes'),
|
||||
className: 'btn-success'
|
||||
},
|
||||
cancel: {
|
||||
label: __t('No'),
|
||||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: function(result)
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Delete('objects/userentities/' + objectId, { },
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/userentities');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
68
public/viewjs/userentityform.js
Normal file
68
public/viewjs/userentityform.js
Normal file
@ -0,0 +1,68 @@
|
||||
$('#save-userentity-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
var jsonData = $('#userentity-form').serializeJSON();
|
||||
Grocy.FrontendHelpers.BeginUiBusy("userentity-form");
|
||||
|
||||
var redirectUrl = U("/userentities");
|
||||
|
||||
if (Grocy.EditMode === 'create')
|
||||
{
|
||||
Grocy.Api.Post('objects/userentities', jsonData,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = redirectUrl;
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.EndUiBusy("userentity-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Api.Put('objects/userentities/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = redirectUrl;
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.EndUiBusy("userentity-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$('#userentity-form input').keyup(function(event)
|
||||
{
|
||||
Grocy.FrontendHelpers.ValidateForm('userentity-form');
|
||||
});
|
||||
|
||||
$('#userentity-form select').change(function(event)
|
||||
{
|
||||
Grocy.FrontendHelpers.ValidateForm('userentity-form');
|
||||
});
|
||||
|
||||
$('#userentity-form input').keydown(function(event)
|
||||
{
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
||||
if (document.getElementById('userentity-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#save-userentity-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#name').focus();
|
||||
Grocy.FrontendHelpers.ValidateForm('userentity-form');
|
47
public/viewjs/userobjectform.js
Normal file
47
public/viewjs/userobjectform.js
Normal file
@ -0,0 +1,47 @@
|
||||
$('#save-userobject-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
var jsonData = {};
|
||||
jsonData.userentity_id = Grocy.EditObjectParentId;
|
||||
console.log(jsonData);
|
||||
Grocy.FrontendHelpers.BeginUiBusy("userobject-form");
|
||||
|
||||
if (Grocy.EditMode === 'create')
|
||||
{
|
||||
Grocy.Api.Post('objects/userobjects', jsonData,
|
||||
function(result)
|
||||
{
|
||||
Grocy.EditObjectId = result.created_object_id;
|
||||
Grocy.Components.UserfieldsForm.Save(function()
|
||||
{
|
||||
window.location.href = U('/userobjects/' + Grocy.EditObjectParentName);
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.EndUiBusy("userobject-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Api.Put('objects/userobjects/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
{
|
||||
Grocy.Components.UserfieldsForm.Save(function()
|
||||
{
|
||||
window.location.href = U('/userobjects/' + Grocy.EditObjectParentName);
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.EndUiBusy("userobject-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Components.UserfieldsForm.Load();
|
68
public/viewjs/userobjects.js
Normal file
68
public/viewjs/userobjects.js
Normal file
@ -0,0 +1,68 @@
|
||||
var userobjectsTable = $('#userobjects-table').DataTable({
|
||||
'paginate': false,
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 }
|
||||
],
|
||||
'language': JSON.parse(__t('datatables_localization')),
|
||||
'scrollY': false,
|
||||
'colReorder': true,
|
||||
'stateSave': true,
|
||||
'stateSaveParams': function(settings, data)
|
||||
{
|
||||
data.search.search = "";
|
||||
|
||||
data.columns.forEach(column =>
|
||||
{
|
||||
column.search.search = "";
|
||||
});
|
||||
}
|
||||
});
|
||||
$('#userobjects-table tbody').removeClass("d-none");
|
||||
userobjectsTable.columns.adjust().draw();
|
||||
|
||||
$("#search").on("keyup", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
if (value === "all")
|
||||
{
|
||||
value = "";
|
||||
}
|
||||
|
||||
userobjectsTable.search(value).draw();
|
||||
});
|
||||
|
||||
$(document).on('click', '.userobject-delete-button', function (e)
|
||||
{
|
||||
var objectId = $(e.currentTarget).attr('data-userobject-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: __t('Are you sure to delete this userobject?'),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: __t('Yes'),
|
||||
className: 'btn-success'
|
||||
},
|
||||
cancel: {
|
||||
label: __t('No'),
|
||||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: function(result)
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Delete('objects/userobjects/' + objectId, { },
|
||||
function(result)
|
||||
{
|
||||
window.location.reload();
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
@ -19,6 +19,10 @@ $app->group('', function()
|
||||
// Generic entity interaction
|
||||
$this->get('/userfields', '\Grocy\Controllers\GenericEntityController:UserfieldsList');
|
||||
$this->get('/userfield/{userfieldId}', '\Grocy\Controllers\GenericEntityController:UserfieldEditForm');
|
||||
$this->get('/userentities', '\Grocy\Controllers\GenericEntityController:UserentitiesList');
|
||||
$this->get('/userentity/{userentityId}', '\Grocy\Controllers\GenericEntityController:UserentityEditForm');
|
||||
$this->get('/userobjects/{userentityName}', '\Grocy\Controllers\GenericEntityController:UserobjectsList');
|
||||
$this->get('/userobject/{userentityName}/{userobjectId}', '\Grocy\Controllers\GenericEntityController:UserobjectEditForm');
|
||||
|
||||
// User routes
|
||||
$this->get('/users', '\Grocy\Controllers\UsersController:UsersList');
|
||||
|
@ -9,7 +9,20 @@ class ApplicationService extends BaseService
|
||||
{
|
||||
if ($this->InstalledVersion == null)
|
||||
{
|
||||
$this->InstalledVersion = json_decode(file_get_contents(__DIR__ . '/../version.json'));
|
||||
if (GROCY_MODE === 'prerelease')
|
||||
{
|
||||
$commitHash = trim(exec('git log --pretty="%h" -n1 HEAD'));
|
||||
$commitDate = trim(exec('git log --date=iso --pretty="%cd" -n1 HEAD'));
|
||||
|
||||
$this->InstalledVersion = array(
|
||||
'Version' => "pre-release-$commitHash",
|
||||
'ReleaseDate' => substr($commitDate, 0, 19)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->InstalledVersion = json_decode(file_get_contents(__DIR__ . '/../version.json'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->InstalledVersion;
|
||||
|
@ -150,6 +150,19 @@ class DemoDataGeneratorService extends BaseService
|
||||
INSERT INTO equipment (name, description, instruction_manual_file_name) VALUES ('{$this->__t_sql('Coffee machine')}', '{$loremIpsumWithHtmlFormattings}', 'loremipsum.pdf'); --1
|
||||
INSERT INTO equipment (name, description) VALUES ('{$this->__t_sql('Dishwasher')}', '{$loremIpsumWithHtmlFormattings}'); --2
|
||||
|
||||
INSERT INTO userentities (name, caption, description, show_in_sidebar_menu, icon_css_class) VALUES ('exampleuserentity', '{$this->__t_sql('Example userentity')}', '{$this->__t_sql('This is an example user entity...')}', 1, 'fas fa-smile'); --1
|
||||
|
||||
INSERT INTO userfields (entity, name, caption, type, show_as_column_in_tables) VALUES ('userentity-exampleuserentity', 'customfield1', '{$this->__t_sql('Custom field')} 1', 'text-single-line', 1); --1
|
||||
INSERT INTO userfields (entity, name, caption, type, show_as_column_in_tables) VALUES ('userentity-exampleuserentity', 'customfield2', '{$this->__t_sql('Custom field')} 2', 'text-single-line', 1); --2
|
||||
|
||||
INSERT INTO userobjects (userentity_id) VALUES (1); --1
|
||||
INSERT INTO userobjects (userentity_id) VALUES (1); --2
|
||||
|
||||
INSERT INTO userfield_values (field_id, object_id, value) VALUES (1, 1, '{$this->__t_sql('Example field value...')}');
|
||||
INSERT INTO userfield_values (field_id, object_id, value) VALUES (2, 1, '{$this->__t_sql('Example field value...')}');
|
||||
INSERT INTO userfield_values (field_id, object_id, value) VALUES (1, 2, '{$this->__t_sql('Example field value...')}');
|
||||
INSERT INTO userfield_values (field_id, object_id, value) VALUES (2, 2, '{$this->__t_sql('Example field value...')}');
|
||||
|
||||
INSERT INTO migrations (migration) VALUES (-1);
|
||||
";
|
||||
|
||||
|
@ -109,7 +109,15 @@ class UserfieldsService extends BaseService
|
||||
|
||||
public function GetEntities()
|
||||
{
|
||||
return $this->OpenApiSpec->components->internalSchemas->ExposedEntity->enum;
|
||||
$exposedDefaultEntities = $this->OpenApiSpec->components->internalSchemas->ExposedEntity->enum;
|
||||
|
||||
$userentities = array();
|
||||
foreach ($this->Database->userentities()->orderBy('name') as $userentity)
|
||||
{
|
||||
$userentities[] = 'userentity-' . $userentity->name;
|
||||
}
|
||||
|
||||
return array_merge($exposedDefaultEntities, $userentities);
|
||||
}
|
||||
|
||||
public function GetFieldTypes()
|
||||
@ -119,6 +127,6 @@ class UserfieldsService extends BaseService
|
||||
|
||||
private function IsValidEntity($entity)
|
||||
{
|
||||
return in_array($entity, $this->OpenApiSpec->components->internalSchemas->ExposedEntity->enum);
|
||||
return in_array($entity, $this->GetEntities());
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +186,17 @@
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@php $firstUserentity = true; @endphp
|
||||
@foreach($userentitiesForSidebar as $userentity)
|
||||
<li class="nav-item @if($firstUserentity) mt-4 @endif" data-toggle="tooltip" data-placement="right" title="{{ $userentity->caption }}" data-nav-for-page="userentity-{{ $userentity->name }}">
|
||||
<a class="nav-link discrete-link" href="{{ $U('/userobjects/' . $userentity->name) }}">
|
||||
<i class="{{ $userentity->icon_css_class }}"></i>
|
||||
<span class="nav-link-text">{{ $userentity->caption }}</span>
|
||||
</a>
|
||||
</li>
|
||||
@php if ($firstUserentity) { $firstUserentity = false; } @endphp
|
||||
@endforeach
|
||||
|
||||
<li class="nav-item mt-4" data-toggle="tooltip" data-placement="right" title="{{ $__t('Manage master data') }}">
|
||||
<a class="nav-link nav-link-collapse collapsed discrete-link" data-toggle="collapse" href="#top-nav-manager-master-data">
|
||||
@ -249,6 +260,12 @@
|
||||
<span class="nav-link-text">{{ $__t('Userfields') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li data-nav-for-page="userentities" data-sub-menu-of="#top-nav-manager-master-data">
|
||||
<a class="nav-link discrete-link" href="{{ $U('/userentities') }}">
|
||||
<i class="fas fa-bookmark "></i>
|
||||
<span class="nav-link-text">{{ $__t('Userentities') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
62
views/userentities.blade.php
Normal file
62
views/userentities.blade.php
Normal file
@ -0,0 +1,62 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@section('title', $__t('Userentities'))
|
||||
@section('activeNav', 'userentities')
|
||||
@section('viewJsName', 'userentities')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1>
|
||||
@yield('title')
|
||||
<a id="new-userentity-button" class="btn btn-outline-dark" href="{{ $U('/userentity/new') }}">
|
||||
<i class="fas fa-plus"></i> {{ $__t('Add') }}
|
||||
</a>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-xs-12 col-md-6 col-xl-3">
|
||||
<label for="search">{{ $__t('Search') }}</label> <i class="fas fa-search"></i>
|
||||
<input type="text" class="form-control" id="search">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="userentities-table" class="table table-sm table-striped dt-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"></th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Caption') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($userentities as $userentity)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm" href="{{ $U('/userentity/') }}{{ $userentity->id }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm userentity-delete-button" href="#" data-userentity-id="{{ $userentity->id }}" data-userentity-name="{{ $userentity->name }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<a class="btn btn-secondary btn-sm" href="{{ $U('/userfields?entity=userentity-') }}{{ $userentity->name }}">
|
||||
<i class="fas fa-th-list"></i> {{ $__t('Configure userfields') }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ $userentity->name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $userentity->caption }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
59
views/userentityform.blade.php
Normal file
59
views/userentityform.blade.php
Normal file
@ -0,0 +1,59 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit userentity'))
|
||||
@else
|
||||
@section('title', $__t('Create userentity'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'userentityform')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-xs-12">
|
||||
<h1>@yield('title')</h1>
|
||||
|
||||
<script>Grocy.EditMode = '{{ $mode }}';</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
<script>Grocy.EditObjectId = {{ $userentity->id }};</script>
|
||||
@endif
|
||||
|
||||
<form id="userentity-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $__t('Name') }}</label>
|
||||
<input @if($mode == 'edit') disabled @endif type="text" class="form-control" required pattern="^[a-zA-Z0-9]*$" id="name" name="name" value="@if($mode == 'edit'){{ $userentity->name }}@endif">
|
||||
<div class="invalid-feedback">{{ $__t('This is required and can only contain letters and numbers') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $__t('Caption') }}</label>
|
||||
<input type="text" class="form-control" required id="caption" name="caption" value="@if($mode == 'edit'){{ $userentity->caption }}@endif">
|
||||
<div class="invalid-feedback">{{ $__t('A caption is required') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">{{ $__t('Description') }}</label>
|
||||
<textarea class="form-control" rows="2" id="description" name="description">@if($mode == 'edit'){{ $userentity->description }}@endif</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input type="hidden" name="show_in_sidebar_menu" value="0">
|
||||
<input @if($mode == 'edit' && $userentity->show_in_sidebar_menu == 1) checked @endif class="form-check-input" type="checkbox" id="show_in_sidebar_menu" name="show_in_sidebar_menu" value="1">
|
||||
<label class="form-check-label" for="show_in_sidebar_menu">{{ $__t('Show in sidebar menu') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $__t('Icon CSS class') }}</label>
|
||||
<input type="text" class="form-control" id="icon_css_class" name="icon_css_class" value="@if($mode == 'edit'){{ $userentity->icon_css_class }}@endif" placeholder='{{ $__t('For example') }} "fas fa-smile"'>
|
||||
</div>
|
||||
|
||||
<button id="save-userentity-button" class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
38
views/userobjectform.blade.php
Normal file
38
views/userobjectform.blade.php
Normal file
@ -0,0 +1,38 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit %s', $userentity->caption))
|
||||
@else
|
||||
@section('title', $__t('Create %s', $userentity->caption))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'userobjectform')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-xs-12">
|
||||
<h1>@yield('title')</h1>
|
||||
|
||||
<script>
|
||||
Grocy.EditMode = '{{ $mode }}';
|
||||
Grocy.EditObjectParentId = {{ $userentity->id }};
|
||||
Grocy.EditObjectParentName = "{{ $userentity->name }}";
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
<script>Grocy.EditObjectId = {{ $userobject->id }};</script>
|
||||
@endif
|
||||
|
||||
<form id="userobject-form" novalidate>
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'userentity-' . $userentity->name
|
||||
))
|
||||
|
||||
<button id="save-userobject-button" class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
66
views/userobjects.blade.php
Normal file
66
views/userobjects.blade.php
Normal file
@ -0,0 +1,66 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@section('title', $userentity->caption)
|
||||
@section('activeNav', 'userentity-' . $userentity->name)
|
||||
@section('viewJsName', 'userobjects')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1>
|
||||
@yield('title')
|
||||
<a class="btn btn-outline-dark" href="{{ $U('/userobject/' . $userentity->name . '/new') }}">
|
||||
<i class="fas fa-plus"></i> {{ $__t('Add') }}
|
||||
</a>
|
||||
<a class="btn btn-outline-secondary" href="{{ $U('/userfields?entity=' . 'userentity-' . $userentity->name) }}">
|
||||
<i class="fas fa-sliders-h"></i> {{ $__t('Configure userfields') }}
|
||||
</a>
|
||||
</h1>
|
||||
<h5 class="text-muted">{{ $userentity->description }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-xs-12 col-md-6 col-xl-3">
|
||||
<label for="search">{{ $__t('Search') }}</label> <i class="fas fa-search"></i>
|
||||
<input type="text" class="form-control" id="search">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="userobjects-table" class="table table-sm table-striped dt-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"></th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($userobjects as $userobject)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm" href="{{ $U('/userobject/' . $userentity->name . '/') }}{{ $userobject->id }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm userobject-delete-button" href="#" data-userobject-id="{{ $userobject->id }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $userobject->id)
|
||||
))
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
Loading…
x
Reference in New Issue
Block a user