mirror of
https://github.com/prurigro/hypothetical.git
synced 2024-11-21 23:52:31 -05:00
Reuse dashboard logic much more, delete images and files when the associated item is deleted, rework dashboard lists so their items can be configured in the $dashboard_columns of their own class, allow dashboard lists to upload images, delete images when dashboard list items are deleted, add a default image extension variable to dashboard model items rather than hard-coding it so it can be reconfigured, improve the dashboard styles some more, and improve the readme (including documenting the new dashboard list update)
This commit is contained in:
parent
ace3c607ca
commit
db7deb0fdc
9 changed files with 397 additions and 166 deletions
|
@ -70,38 +70,19 @@ class Dashboard
|
||||||
*/
|
*/
|
||||||
public static function getModel($model, $type = null)
|
public static function getModel($model, $type = null)
|
||||||
{
|
{
|
||||||
$model_name = ucfirst($model);
|
$model_name = implode('', array_map('ucfirst', explode('_', $model)));
|
||||||
|
|
||||||
// Ensure the model has been declared in the menu
|
if (file_exists(app_path() . '/Models/' . $model_name . '.php')) {
|
||||||
$model_in_menu = false;
|
|
||||||
|
|
||||||
foreach (self::$menu as $menu_item) {
|
|
||||||
if (array_key_exists('submenu', $menu_item)) {
|
|
||||||
// Check each item if this is a submenu
|
|
||||||
foreach ($menu_item['submenu'] as $submenu_item) {
|
|
||||||
if ($submenu_item['model'] == $model) {
|
|
||||||
$model_in_menu = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Check the menu item
|
|
||||||
if ($menu_item['model'] == $model) {
|
|
||||||
$model_in_menu = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't bother continuing if we've already confirmed it's in the menu
|
|
||||||
if ($model_in_menu) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($model_in_menu && file_exists(app_path() . '/Models/' . $model_name . '.php')) {
|
|
||||||
$model_class = 'App\\Models\\' . $model_name;
|
$model_class = 'App\\Models\\' . $model_name;
|
||||||
|
|
||||||
if ($type != null && $type != $model_class::$dashboard_type) {
|
if ($type != null) {
|
||||||
return null;
|
if (is_array($type)) {
|
||||||
|
if (!in_array($model_class::$dashboard_type, $type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if ($type != $model_class::$dashboard_type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new $model_class;
|
return new $model_class;
|
||||||
|
|
|
@ -82,18 +82,11 @@ class DashboardController extends Controller {
|
||||||
|
|
||||||
if ($model_class != null) {
|
if ($model_class != null) {
|
||||||
if ($id == 'new') {
|
if ($id == 'new') {
|
||||||
$item = null;
|
$item = new $model_class;
|
||||||
} else {
|
} else {
|
||||||
if ($model_class::where('id', $id)->exists()) {
|
if ($model_class::where('id', $id)->exists()) {
|
||||||
$item = $model_class::find($id);
|
$item = $model_class::find($id);
|
||||||
|
|
||||||
foreach ($model_class::$dashboard_columns as $column) {
|
|
||||||
if ($column['type'] === 'list') {
|
|
||||||
$list_model_class = 'App\\Models\\' . $column['model'];
|
|
||||||
$item->{$column['name']} = $list_model_class::where($column['foreign'], $item->id)->orderBy($column['sort'])->get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($item) || !$item->userCheck()) {
|
if (is_null($item) || !$item->userCheck()) {
|
||||||
return view('errors.no-such-record');
|
return view('errors.no-such-record');
|
||||||
}
|
}
|
||||||
|
@ -102,13 +95,26 @@ class DashboardController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($model_class::$dashboard_columns as $column) {
|
||||||
|
if ($column['type'] === 'list') {
|
||||||
|
$list_model_class = 'App\\Models\\' . $column['model'];
|
||||||
|
$list_model_instance = new $list_model_class;
|
||||||
|
|
||||||
|
$item->{$column['name']} = [
|
||||||
|
'model' => $list_model_instance->getTable(),
|
||||||
|
'list' => $id == 'new' ? [] : $list_model_instance::where($column['foreign'], $item->id)->orderBy($column['sort'])->get()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view('dashboard.pages.edit-item', [
|
return view('dashboard.pages.edit-item', [
|
||||||
'heading' => $model_class->getDashboardHeading(),
|
'heading' => $model_class->getDashboardHeading(),
|
||||||
'model' => $model,
|
'default_img_ext' => $model_class::$default_image_ext,
|
||||||
'id' => $id,
|
'model' => $model,
|
||||||
'item' => $item,
|
'id' => $id,
|
||||||
'help_text' => $model_class::$dashboard_help_text,
|
'item' => $item,
|
||||||
'columns' => $model_class::$dashboard_columns
|
'help_text' => $model_class::$dashboard_help_text,
|
||||||
|
'columns' => $model_class::$dashboard_columns
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
abort(404);
|
abort(404);
|
||||||
|
@ -242,6 +248,8 @@ class DashboardController extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate connected lists with list items in $request
|
// populate connected lists with list items in $request
|
||||||
|
$lists = [];
|
||||||
|
|
||||||
foreach ($request['columns'] as $column) {
|
foreach ($request['columns'] as $column) {
|
||||||
if ($column['type'] === 'list') {
|
if ($column['type'] === 'list') {
|
||||||
$column_name = $column['name'];
|
$column_name = $column['name'];
|
||||||
|
@ -250,20 +258,44 @@ class DashboardController extends Controller {
|
||||||
if ($dashboard_column['name'] === $column_name) {
|
if ($dashboard_column['name'] === $column_name) {
|
||||||
$foreign = $dashboard_column['foreign'];
|
$foreign = $dashboard_column['foreign'];
|
||||||
$list_model_class = 'App\\Models\\' . $dashboard_column['model'];
|
$list_model_class = 'App\\Models\\' . $dashboard_column['model'];
|
||||||
$list_model_class::where($foreign, $item->id)->delete();
|
|
||||||
|
|
||||||
if ($request->has($column_name)) {
|
if ($list_model_class::$dashboard_type == 'list') {
|
||||||
foreach ($request[$column_name] as $index => $row) {
|
$ids = [];
|
||||||
$list_model_item = new $list_model_class;
|
|
||||||
$list_model_item->$foreign = $item->id;
|
|
||||||
$list_model_item->{$dashboard_column['sort']} = $index;
|
|
||||||
|
|
||||||
foreach ($row as $key => $value) {
|
if ($request->has($column_name)) {
|
||||||
$list_model_item->$key = $value;
|
foreach ($request[$column_name] as $index => $row) {
|
||||||
|
if ($row['id'] == 'new') {
|
||||||
|
$list_model_item = new $list_model_class;
|
||||||
|
} else {
|
||||||
|
$list_model_item = $list_model_class::find($row['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$list_model_item->$foreign = $item->id;
|
||||||
|
$list_model_item->{$dashboard_column['sort']} = $index;
|
||||||
|
|
||||||
|
foreach ($row['data'] as $key => $data) {
|
||||||
|
if ($data['type'] == 'string') {
|
||||||
|
$list_model_item->$key = $data['value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$list_model_item->save();
|
||||||
|
array_push($ids, $list_model_item->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$list_model_item->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete any associated row that wasn't just created or edited
|
||||||
|
foreach ($list_model_class::where($foreign, $item->id)->whereNotIn('id', $ids)->get() as $list_item) {
|
||||||
|
$list_item->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the sets of ids for each list
|
||||||
|
$lists[$column_name] = $ids;
|
||||||
|
|
||||||
|
// stop looping through dashboard columns
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return 'invalid-list-model:' . $dashboard_column['model'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,7 +306,10 @@ class DashboardController extends Controller {
|
||||||
$item->save();
|
$item->save();
|
||||||
|
|
||||||
// return the id number in the format '^id:[0-9][0-9]*$' on success
|
// return the id number in the format '^id:[0-9][0-9]*$' on success
|
||||||
return 'id:' . $item->id;
|
return [
|
||||||
|
'id' => $item->id,
|
||||||
|
'lists' => $lists
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
return 'model-access-fail';
|
return 'model-access-fail';
|
||||||
}
|
}
|
||||||
|
@ -289,7 +324,7 @@ class DashboardController extends Controller {
|
||||||
'name' => 'required'
|
'name' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$model_class = Dashboard::getModel($request['model'], 'edit');
|
$model_class = Dashboard::getModel($request['model'], [ 'edit', 'list' ]);
|
||||||
|
|
||||||
if ($model_class != null) {
|
if ($model_class != null) {
|
||||||
$item = $model_class::find($request['id']);
|
$item = $model_class::find($request['id']);
|
||||||
|
@ -321,7 +356,7 @@ class DashboardController extends Controller {
|
||||||
'name' => 'required'
|
'name' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$model_class = Dashboard::getModel($request['model'], 'edit');
|
$model_class = Dashboard::getModel($request['model'], [ 'edit', 'list' ]);
|
||||||
|
|
||||||
if ($model_class != null) {
|
if ($model_class != null) {
|
||||||
$item = $model_class::find($request['id']);
|
$item = $model_class::find($request['id']);
|
||||||
|
@ -363,12 +398,16 @@ class DashboardController extends Controller {
|
||||||
return 'permission-fail';
|
return 'permission-fail';
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete associated files if they exist
|
// delete associated list items
|
||||||
foreach ($model_class::$dashboard_columns as $column) {
|
foreach ($item::$dashboard_columns as $column) {
|
||||||
if ($column['type'] == 'image') {
|
if ($column['type'] == 'list') {
|
||||||
$item->deleteImage($column['name'], false);
|
$list_model_class = Dashboard::getModel($column['model'], 'list');
|
||||||
} else if ($column['type'] == 'file') {
|
|
||||||
$item->deleteFile($column['name'], false);
|
if ($list_model_class != null) {
|
||||||
|
foreach ($list_model_class::where($column['foreign'], $item->id)->get() as $list_item) {
|
||||||
|
$list_item->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +438,7 @@ class DashboardController extends Controller {
|
||||||
'name' => 'required'
|
'name' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$model_class = Dashboard::getModel($request['model'], 'edit');
|
$model_class = Dashboard::getModel($request['model'], [ 'edit', 'list' ]);
|
||||||
|
|
||||||
if ($model_class != null) {
|
if ($model_class != null) {
|
||||||
$item = $model_class::find($request['id']);
|
$item = $model_class::find($request['id']);
|
||||||
|
@ -423,7 +462,7 @@ class DashboardController extends Controller {
|
||||||
'name' => 'required'
|
'name' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$model_class = Dashboard::getModel($request['model'], 'edit');
|
$model_class = Dashboard::getModel($request['model'], [ 'edit', 'list' ]);
|
||||||
|
|
||||||
if ($model_class != null) {
|
if ($model_class != null) {
|
||||||
$item = $model_class::find($request['id']);
|
$item = $model_class::find($request['id']);
|
||||||
|
|
|
@ -21,8 +21,8 @@ class Blog extends DashboardModel
|
||||||
[ 'name' => 'created_at', 'title' => 'Date', 'type' => 'display' ],
|
[ 'name' => 'created_at', 'title' => 'Date', 'type' => 'display' ],
|
||||||
[ 'name' => 'title', 'required' => true, 'unique' => true, 'type' => 'string' ],
|
[ 'name' => 'title', 'required' => true, 'unique' => true, 'type' => 'string' ],
|
||||||
[ 'name' => 'body', 'required' => true, 'type' => 'mkd' ],
|
[ 'name' => 'body', 'required' => true, 'type' => 'mkd' ],
|
||||||
[ 'name' => 'header-image', 'title' => 'Header Image', 'type' => 'image', 'delete' => true ],
|
[ 'name' => 'header-image', 'title' => 'Header Image', 'type' => 'image', 'delete' => true, 'ext' => 'jpg' ],
|
||||||
[ 'name' => 'tags', 'type' => 'list', 'model' => 'BlogTags', 'columns' => [ 'name' ], 'foreign' => 'blog_id', 'sort' => 'order' ]
|
[ 'name' => 'tags', 'type' => 'list', 'model' => 'BlogTags', 'foreign' => 'blog_id', 'sort' => 'order' ]
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function getBlogEntries()
|
public static function getBlogEntries()
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class BlogTags extends Model {
|
class BlogTags extends DashboardModel
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* The database table used by the model.
|
* The database table used by the model.
|
||||||
*
|
*
|
||||||
|
@ -11,4 +11,9 @@ class BlogTags extends Model {
|
||||||
*/
|
*/
|
||||||
protected $table = 'blog_tags';
|
protected $table = 'blog_tags';
|
||||||
|
|
||||||
|
public static $dashboard_type = 'list';
|
||||||
|
|
||||||
|
public static $dashboard_columns = [
|
||||||
|
[ 'type' => 'string', 'name' => 'name' ]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,33 @@ class DashboardModel extends Model
|
||||||
*/
|
*/
|
||||||
public static $dashboard_id_link = [];
|
public static $dashboard_id_link = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default image extension when none is set
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $default_image_ext = 'jpg';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functionality to run when various events occur
|
||||||
|
*
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
public static function boot() {
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
static::deleting(function($item) {
|
||||||
|
// delete associated images and files if they exist
|
||||||
|
foreach ($item::$dashboard_columns as $column) {
|
||||||
|
if ($column['type'] == 'image') {
|
||||||
|
$item->deleteImage($column['name'], false);
|
||||||
|
} else if ($column['type'] == 'file') {
|
||||||
|
$item->deleteFile($column['name'], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the dashboard heading
|
* Returns the dashboard heading
|
||||||
*
|
*
|
||||||
|
@ -155,7 +182,6 @@ class DashboardModel extends Model
|
||||||
|
|
||||||
$max_width = 0;
|
$max_width = 0;
|
||||||
$max_height = 0;
|
$max_height = 0;
|
||||||
$main_ext = 'jpg';
|
|
||||||
|
|
||||||
// Retrieve the column
|
// Retrieve the column
|
||||||
$column = static::getColumn($name);
|
$column = static::getColumn($name);
|
||||||
|
@ -165,9 +191,11 @@ class DashboardModel extends Model
|
||||||
return 'no-such-column-fail';
|
return 'no-such-column-fail';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the extension if it's been configured
|
// Use the configured image extension or fall back on the default if none is set
|
||||||
if (array_key_exists('ext', $column)) {
|
if (array_key_exists('ext', $column)) {
|
||||||
$main_ext = $column['ext'];
|
$main_ext = $column['ext'];
|
||||||
|
} else {
|
||||||
|
$main_ext = $this::$default_image_ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the directory if it doesn't exist
|
// Create the directory if it doesn't exist
|
||||||
|
@ -254,7 +282,6 @@ class DashboardModel extends Model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up our variables
|
// Set up our variables
|
||||||
$main_ext = 'jpg';
|
|
||||||
$extensions = [];
|
$extensions = [];
|
||||||
|
|
||||||
// Retrieve the column
|
// Retrieve the column
|
||||||
|
@ -265,9 +292,11 @@ class DashboardModel extends Model
|
||||||
return 'no-such-column-fail';
|
return 'no-such-column-fail';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the extension if it's been configured
|
// Use the configured image extension or fall back on the default if none is set
|
||||||
if (array_key_exists('ext', $column)) {
|
if (array_key_exists('ext', $column)) {
|
||||||
$main_ext = $column['ext'];
|
$main_ext = $column['ext'];
|
||||||
|
} else {
|
||||||
|
$main_ext = $this::$default_image_ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the set of extensions to delete
|
// Build the set of extensions to delete
|
||||||
|
|
47
readme.md
47
readme.md
|
@ -14,12 +14,13 @@ A Hypothetical website template for bootstrapping new projects.
|
||||||
## Major Components
|
## Major Components
|
||||||
|
|
||||||
* Bootstrap 5
|
* Bootstrap 5
|
||||||
* Fontawesome 5
|
* Browsersync
|
||||||
* Gsap 3
|
* Fontawesome
|
||||||
* Gulp 4
|
* Gsap
|
||||||
* Jquery 3
|
* Gulp
|
||||||
|
* Jquery
|
||||||
* Laravel 9
|
* Laravel 9
|
||||||
* Sass 1.32
|
* Sass
|
||||||
* Vue 3 (Optional)
|
* Vue 3 (Optional)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
@ -71,13 +72,13 @@ Reading through its contents is encouraged for a complete understanding of what
|
||||||
|
|
||||||
* `gulp`: Update the compiled javascript and css in `public/js` and `public/css`, and copy fonts to `public/fonts`.
|
* `gulp`: Update the compiled javascript and css in `public/js` and `public/css`, and copy fonts to `public/fonts`.
|
||||||
* `gulp --production`: Does the same as `gulp` except the compiled javascript and css is minified, and console logging is removed from the javascript (good for production deployments).
|
* `gulp --production`: Does the same as `gulp` except the compiled javascript and css is minified, and console logging is removed from the javascript (good for production deployments).
|
||||||
* `gulp default watch`: Does the same as `gulp` but continues running to watch for changes to files so it can recompile updated assets and reload them in the browser using BrowserSync (good for development environments).
|
* `gulp default watch`: Does the same as `gulp` but continues running to watch for changes to files so it can recompile updated assets and reload them in the browser using Browsersync (good for development environments).
|
||||||
|
|
||||||
**NOTE**: If `gulp` isn't installed globally or its version is less than `4`, you should use the version included in `node_modules` by running `"$(npm bin)/gulp"` in place of the `gulp` command.
|
**NOTE**: If `gulp` isn't installed globally or its version is less than `4`, you should use the version included in `node_modules` by running `"$(npm bin)/gulp"` in place of the `gulp` command.
|
||||||
|
|
||||||
### BrowserSync
|
### Browsersync
|
||||||
|
|
||||||
BrowserSync is used to keep the browser in sync with your code when running the `watch` task with gulp.
|
Browsersync is used to keep the browser in sync with your code when running the `watch` task with gulp.
|
||||||
|
|
||||||
## Public
|
## Public
|
||||||
|
|
||||||
|
@ -148,6 +149,13 @@ In a Vue.js component:
|
||||||
|
|
||||||
## Dashboard
|
## Dashboard
|
||||||
|
|
||||||
|
### Important Note
|
||||||
|
|
||||||
|
The naming convention of dashboard database tables and model classes should be the following:
|
||||||
|
|
||||||
|
* Database table names should be lower case with hyphen separators: `your_table_name`
|
||||||
|
* Model classes should be the same name but in camel case with its first character capitalized: `YourTableName.php` and `class YourTableName extends DashboardModel`
|
||||||
|
|
||||||
### Registration
|
### Registration
|
||||||
|
|
||||||
The `REGISTRATION` variable in the `.env` file controls whether a new dashboard user can be registered.
|
The `REGISTRATION` variable in the `.env` file controls whether a new dashboard user can be registered.
|
||||||
|
@ -229,7 +237,7 @@ Models with their `$dashboard_type` set to `edit` also use:
|
||||||
* `date-time`: Date and time selection tool for date/time data
|
* `date-time`: Date and time selection tool for date/time data
|
||||||
* `mkd`: Multi-line text input field with a markdown editor
|
* `mkd`: Multi-line text input field with a markdown editor
|
||||||
* `select`: Text input via option select
|
* `select`: Text input via option select
|
||||||
* `list`: One or more items saved to a connected table
|
* `list`: One or more `text` or `image` items saved to a connected table
|
||||||
* `image`: Fields that contain image uploads
|
* `image`: Fields that contain image uploads
|
||||||
* `file`: Fields that contains file uploads
|
* `file`: Fields that contains file uploads
|
||||||
* `display`: Displayed information that can't be edited
|
* `display`: Displayed information that can't be edited
|
||||||
|
@ -240,9 +248,19 @@ Models with their `$dashboard_type` set to `edit` also use:
|
||||||
* `name`: (required by `file` and `image`) Used along with the record id to determine the filename
|
* `name`: (required by `file` and `image`) Used along with the record id to determine the filename
|
||||||
* `delete`: (optional for `file` and `image`) Enables a delete button for the upload when set to true
|
* `delete`: (optional for `file` and `image`) Enables a delete button for the upload when set to true
|
||||||
* `ext`: (required by `file` and optional for `image`) Configures the file extension of the upload (`image` defaults to `jpg`)
|
* `ext`: (required by `file` and optional for `image`) Configures the file extension of the upload (`image` defaults to `jpg`)
|
||||||
|
* `model`: (required by `list`) The class name of the model that the list will be generated from
|
||||||
|
* `foreign` (required by `list`) The name of the list table's foreign id column that references the id on the current table
|
||||||
|
* `sort` (required by `list`) The name of the list table's column that the order will be stored in
|
||||||
* `max_width`: (optional for `image`) Configures the maximum width of an image upload (defaults to `0` which sets no maximum width)
|
* `max_width`: (optional for `image`) Configures the maximum width of an image upload (defaults to `0` which sets no maximum width)
|
||||||
* `max_height`: (optional for `image`) Configures the maximum height of an image upload (defaults to `0` which sets no maximum height)
|
* `max_height`: (optional for `image`) Configures the maximum height of an image upload (defaults to `0` which sets no maximum height)
|
||||||
|
|
||||||
|
Models with their `$dashboard_type` set to `list` also use:
|
||||||
|
|
||||||
|
* `type`: The column type which can be any of the following:
|
||||||
|
* `string`: Single-line text input field
|
||||||
|
* `image`: Fields that contain image uploads
|
||||||
|
* `ext`: (optional for `image`) Configures the file extension of the upload (`image` defaults to `jpg`)
|
||||||
|
|
||||||
An example of the `$dashboard_columns` array in a model with its `$dashboard_type` set to `view`:
|
An example of the `$dashboard_columns` array in a model with its `$dashboard_type` set to `view`:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
@ -262,6 +280,15 @@ An example of the `$dashboard_columns` array in a model with its `$dashboard_typ
|
||||||
[ 'name' => 'title', 'required' => true, 'unique' => true, 'type' => 'string' ],
|
[ 'name' => 'title', 'required' => true, 'unique' => true, 'type' => 'string' ],
|
||||||
[ 'name' => 'body', 'required' => true, 'type' => 'mkd' ],
|
[ 'name' => 'body', 'required' => true, 'type' => 'mkd' ],
|
||||||
[ 'name' => 'header-image', 'title' => 'Header Image', 'type' => 'image', 'delete' => true, 'ext' => 'jpg' ],
|
[ 'name' => 'header-image', 'title' => 'Header Image', 'type' => 'image', 'delete' => true, 'ext' => 'jpg' ],
|
||||||
[ 'name' => 'tags', 'type' => 'list', 'model' => 'BlogTags', 'columns' => [ 'name' ], 'foreign' => 'blog_id', 'sort' => 'order' ]
|
[ 'name' => 'tags', 'type' => 'list', 'model' => 'BlogTags', 'foreign' => 'blog_id', 'sort' => 'order' ]
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
An example of the `$dashboard_columns` array in a model with its `$dashboard_type` set to `list`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public static $dashboard_columns = [
|
||||||
|
[ 'type' => 'string', 'name' => 'name' ],
|
||||||
|
[ 'type' => 'image', 'name' => 'photo' ]
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
|
@ -323,17 +323,17 @@ function editItemInit() {
|
||||||
$currencyInputs = $(".currency-input"),
|
$currencyInputs = $(".currency-input"),
|
||||||
$datePickers = $(".date-picker"),
|
$datePickers = $(".date-picker"),
|
||||||
$mkdEditors = $(".mkd-editor"),
|
$mkdEditors = $(".mkd-editor"),
|
||||||
$fileUploads = $(".file-upload"),
|
|
||||||
$imgUploads = $(".image-upload"),
|
|
||||||
$lists = $(".list"),
|
$lists = $(".list"),
|
||||||
$token = $("#token"),
|
$token = $("#token"),
|
||||||
model = $editItem.data("model"),
|
model = $editItem.data("model"),
|
||||||
id = $editItem.data("id"),
|
operation = $editItem.data("id") === "new" ? "create" : "update";
|
||||||
operation = id === "new" ? "create" : "update";
|
|
||||||
|
|
||||||
let allowTimes = [],
|
let $imgUploads = [],
|
||||||
|
$fileUploads = [],
|
||||||
|
allowTimes = [],
|
||||||
easymde = [],
|
easymde = [],
|
||||||
formData = {},
|
formData = {},
|
||||||
|
id = $editItem.data("id"),
|
||||||
submitting = false,
|
submitting = false,
|
||||||
hours,
|
hours,
|
||||||
minutes,
|
minutes,
|
||||||
|
@ -404,30 +404,33 @@ function editItemInit() {
|
||||||
value = [];
|
value = [];
|
||||||
|
|
||||||
$this.find(".list-items .list-items-row").each(function(index, row) {
|
$this.find(".list-items .list-items-row").each(function(index, row) {
|
||||||
const rowData = {};
|
const rowData = {},
|
||||||
|
id = $(row).data("id");
|
||||||
|
|
||||||
$(row).find(".list-items-row-input-inner").each(function(index, input) {
|
$(row).find(".list-items-row-content-inner").each(function(index, inner) {
|
||||||
const $input = $(input),
|
const $inner = $(inner),
|
||||||
column = $input.data("column"),
|
$input = $inner.find(".list-input"),
|
||||||
value = $input.val();
|
column = $inner.data("column");
|
||||||
|
|
||||||
rowData[column] = value;
|
if ($inner.data("type") === "string") {
|
||||||
|
rowData[column] = { type: "string", value: $input.val() };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
value.push(rowData);
|
value.push({ id: typeof id === "undefined" ? "new" : id, data: rowData });
|
||||||
});
|
});
|
||||||
|
|
||||||
addFormData("list", column, value);
|
addFormData("list", column, value);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadFile = function(row_id, currentFile) {
|
const uploadFile = function(currentFile) {
|
||||||
let file, fileUpload;
|
let file, fileUpload;
|
||||||
|
|
||||||
// functionality to run on success
|
// functionality to run on success
|
||||||
const returnSuccess = function() {
|
const returnSuccess = function() {
|
||||||
loadingModal("hide");
|
loadingModal("hide");
|
||||||
window.location.href = `/dashboard/edit/${model}/${row_id}`;
|
window.location.href = `/dashboard/edit/${model}/${id}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// add the file from the file upload box for file-upload class elements
|
// add the file from the file upload box for file-upload class elements
|
||||||
|
@ -439,9 +442,9 @@ function editItemInit() {
|
||||||
|
|
||||||
// add the file, id and model to the formData variable
|
// add the file, id and model to the formData variable
|
||||||
file.append("file", fileUpload.files[0]);
|
file.append("file", fileUpload.files[0]);
|
||||||
file.append("name", $(fileUpload).attr("name"));
|
file.append("name", $(fileUpload).data("column"));
|
||||||
file.append("id", row_id);
|
file.append("id", $(fileUpload).data("id"));
|
||||||
file.append("model", model);
|
file.append("model", $(fileUpload).data("model"));
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
|
@ -452,7 +455,7 @@ function editItemInit() {
|
||||||
beforeSend: function(xhr) { xhr.setRequestHeader("X-CSRF-TOKEN", $token.val()); }
|
beforeSend: function(xhr) { xhr.setRequestHeader("X-CSRF-TOKEN", $token.val()); }
|
||||||
}).always(function(response) {
|
}).always(function(response) {
|
||||||
if (response === "success") {
|
if (response === "success") {
|
||||||
uploadFile(row_id, currentFile + 1);
|
uploadFile(currentFile + 1);
|
||||||
} else {
|
} else {
|
||||||
loadingModal("hide");
|
loadingModal("hide");
|
||||||
|
|
||||||
|
@ -462,19 +465,19 @@ function editItemInit() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
uploadFile(row_id, currentFile + 1);
|
uploadFile(currentFile + 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
returnSuccess();
|
returnSuccess();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadImage = function(row_id, currentImage) {
|
const uploadImage = function(currentImage) {
|
||||||
let file, imgUpload;
|
let file, imgUpload;
|
||||||
|
|
||||||
// functionality to run on success
|
// functionality to run on success
|
||||||
const returnSuccess = function() {
|
const returnSuccess = function() {
|
||||||
uploadFile(row_id, 0);
|
uploadFile(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// add the image from the image upload box for image-upload class elements
|
// add the image from the image upload box for image-upload class elements
|
||||||
|
@ -486,9 +489,9 @@ function editItemInit() {
|
||||||
|
|
||||||
// add the file, id and model to the formData variable
|
// add the file, id and model to the formData variable
|
||||||
file.append("file", imgUpload.files[0]);
|
file.append("file", imgUpload.files[0]);
|
||||||
file.append("name", $(imgUpload).attr("name"));
|
file.append("name", $(imgUpload).data("column"));
|
||||||
file.append("id", row_id);
|
file.append("id", $(imgUpload).data("id"));
|
||||||
file.append("model", model);
|
file.append("model", $(imgUpload).data("model"));
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
|
@ -499,7 +502,7 @@ function editItemInit() {
|
||||||
beforeSend: function(xhr) { xhr.setRequestHeader("X-CSRF-TOKEN", $token.val()); }
|
beforeSend: function(xhr) { xhr.setRequestHeader("X-CSRF-TOKEN", $token.val()); }
|
||||||
}).always(function(response) {
|
}).always(function(response) {
|
||||||
if (response === "success") {
|
if (response === "success") {
|
||||||
uploadImage(row_id, currentImage + 1);
|
uploadImage(currentImage + 1);
|
||||||
} else {
|
} else {
|
||||||
loadingModal("hide");
|
loadingModal("hide");
|
||||||
|
|
||||||
|
@ -509,7 +512,7 @@ function editItemInit() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
uploadImage(row_id, currentImage + 1);
|
uploadImage(currentImage + 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
returnSuccess();
|
returnSuccess();
|
||||||
|
@ -524,7 +527,7 @@ function editItemInit() {
|
||||||
// initialize image deletion
|
// initialize image deletion
|
||||||
$(".edit-button.delete.image").on("click", function(e) {
|
$(".edit-button.delete.image").on("click", function(e) {
|
||||||
const $this = $(this),
|
const $this = $(this),
|
||||||
name = $this.data("name");
|
name = $this.data("column");
|
||||||
|
|
||||||
if (!submitting) {
|
if (!submitting) {
|
||||||
submitting = true;
|
submitting = true;
|
||||||
|
@ -535,8 +538,8 @@ function editItemInit() {
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
url: "/dashboard/image-delete",
|
url: "/dashboard/image-delete",
|
||||||
data: {
|
data: {
|
||||||
id: id,
|
id: $this.data("id"),
|
||||||
model: model,
|
model: $this.data("model"),
|
||||||
name: name,
|
name: name,
|
||||||
_token: $token.val()
|
_token: $token.val()
|
||||||
}
|
}
|
||||||
|
@ -559,7 +562,7 @@ function editItemInit() {
|
||||||
// initialize file deletion
|
// initialize file deletion
|
||||||
$(".edit-button.delete.file").on("click", function(e) {
|
$(".edit-button.delete.file").on("click", function(e) {
|
||||||
const $this = $(this),
|
const $this = $(this),
|
||||||
name = $this.data("name"),
|
name = $this.data("column"),
|
||||||
ext = $this.data("ext");
|
ext = $this.data("ext");
|
||||||
|
|
||||||
if (!submitting) {
|
if (!submitting) {
|
||||||
|
@ -571,8 +574,8 @@ function editItemInit() {
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
url: "/dashboard/file-delete",
|
url: "/dashboard/file-delete",
|
||||||
data: {
|
data: {
|
||||||
id: id,
|
id: $this.data("id"),
|
||||||
model: model,
|
model: $this.data("model"),
|
||||||
name: name,
|
name: name,
|
||||||
ext: ext,
|
ext: ext,
|
||||||
_token: $token.val()
|
_token: $token.val()
|
||||||
|
@ -617,7 +620,7 @@ function editItemInit() {
|
||||||
const $row = $(row);
|
const $row = $(row);
|
||||||
|
|
||||||
// initialize delete button functionality
|
// initialize delete button functionality
|
||||||
$row.find(".list-items-row-button").off("click").on("click", function() {
|
$row.find(".list-items-row-buttons-delete").off("click").on("click", function() {
|
||||||
$row.remove();
|
$row.remove();
|
||||||
initSort();
|
initSort();
|
||||||
contentChanged();
|
contentChanged();
|
||||||
|
@ -627,24 +630,40 @@ function editItemInit() {
|
||||||
|
|
||||||
const initList = function() {
|
const initList = function() {
|
||||||
$list.find(".list-data-row").each(function(rowIndex, row) {
|
$list.find(".list-data-row").each(function(rowIndex, row) {
|
||||||
// Add the values from the current data row to the template
|
const id = $(row).data("id");
|
||||||
|
|
||||||
|
// add the values from the current data row to the template
|
||||||
$(row).find(".list-data-row-item").each(function(itemIndex, item) {
|
$(row).find(".list-data-row-item").each(function(itemIndex, item) {
|
||||||
const $item = $(item),
|
const $item = $(item),
|
||||||
column = $item.data("column"),
|
column = $item.data("column"),
|
||||||
value = $item.data("value");
|
value = $item.data("value"),
|
||||||
|
type = $item.data("type");
|
||||||
|
|
||||||
$template.find(".list-items-row-input-inner").each(function(inputIndex, input) {
|
$template.find(".list-items-row-content-inner").each(function(inputIndex, inner) {
|
||||||
const $input = $(input);
|
const $inner = $(inner);
|
||||||
|
|
||||||
if ($input.data("column") === column) {
|
if ($inner.data("column") === column) {
|
||||||
$input.val(value);
|
if (type === "string") {
|
||||||
|
$inner.find(".list-input").val(value);
|
||||||
|
} else if (type === "image" && value !== "") {
|
||||||
|
$inner.find(".image-link").attr("href", value).addClass("active");
|
||||||
|
$inner.find(".image-preview").attr("src", value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the populated template to the list of items then clear the template values
|
// add the populated template to the list of items
|
||||||
$template.find(".list-items-row").clone().appendTo($items);
|
$template.find(".list-items-row").clone().appendTo($items);
|
||||||
$template.find(".list-items-row-input-inner").val("");
|
|
||||||
|
// set the id for the list items row
|
||||||
|
$items.find(".list-items-row").last().data("id", id);
|
||||||
|
$items.find(".list-items-row").last().find(".list-input").data("id", id);
|
||||||
|
|
||||||
|
// clear the template values
|
||||||
|
$template.find(".list-input").val("");
|
||||||
|
$template.find(".image-link").attr("href", "").removeClass("active");
|
||||||
|
$template.find(".image-preview").attr("src", "");
|
||||||
});
|
});
|
||||||
|
|
||||||
initSort();
|
initSort();
|
||||||
|
@ -740,6 +759,10 @@ function editItemInit() {
|
||||||
if (!submitting && changes) {
|
if (!submitting && changes) {
|
||||||
submitting = true;
|
submitting = true;
|
||||||
|
|
||||||
|
// find the image and file upload inputs
|
||||||
|
$imgUploads = $(".image-upload");
|
||||||
|
$fileUploads = $(".file-upload");
|
||||||
|
|
||||||
// show the loading modal
|
// show the loading modal
|
||||||
loadingModal("show");
|
loadingModal("show");
|
||||||
|
|
||||||
|
@ -755,8 +778,26 @@ function editItemInit() {
|
||||||
}).always(function(response) {
|
}).always(function(response) {
|
||||||
let message = "";
|
let message = "";
|
||||||
|
|
||||||
if ((/^id:[0-9][0-9]*$/).test(response)) {
|
if (typeof response.id !== "undefined") {
|
||||||
uploadImage(response.replace(/^id:/, ""), 0);
|
id = response.id;
|
||||||
|
|
||||||
|
// Add the appropriate ids to each list item input
|
||||||
|
if (Object.keys(response.lists).length) {
|
||||||
|
Object.keys(response.lists).forEach(function(key) {
|
||||||
|
$(`#${key}`).find(".list-items").first().find(".list-items-row").each(function(index) {
|
||||||
|
const listItemId = response.lists[key][index];
|
||||||
|
|
||||||
|
$(this).data("id", listItemId);
|
||||||
|
$(this).find(".list-input").data("id", listItemId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current row id to each image and file upload input
|
||||||
|
$(".image-upload, .file-upload").not(".list-input").data("id", response.id);
|
||||||
|
|
||||||
|
// Start uploading images (and then files)
|
||||||
|
uploadImage(0);
|
||||||
} else {
|
} else {
|
||||||
loadingModal("hide");
|
loadingModal("hide");
|
||||||
|
|
||||||
|
@ -764,6 +805,8 @@ function editItemInit() {
|
||||||
message = `<strong>${response.replace(/'/g, "").replace(/^[^:]*:/, "").replace(/,([^,]*)$/, "</strong> and <strong>$1").replace(/,/g, "</strong>, <strong>")}</strong> must be unique`;
|
message = `<strong>${response.replace(/'/g, "").replace(/^[^:]*:/, "").replace(/,([^,]*)$/, "</strong> and <strong>$1").replace(/,/g, "</strong>, <strong>")}</strong> must be unique`;
|
||||||
} else if ((/^required:/).test(response)) {
|
} else if ((/^required:/).test(response)) {
|
||||||
message = `<strong>${response.replace(/'/g, "").replace(/^[^:]*:/, "").replace(/,([^,]*)$/, "</strong> and <strong>$1").replace(/,/g, "</strong>, <strong>")}</strong> must not be empty`;
|
message = `<strong>${response.replace(/'/g, "").replace(/^[^:]*:/, "").replace(/,([^,]*)$/, "</strong> and <strong>$1").replace(/,/g, "</strong>, <strong>")}</strong> must not be empty`;
|
||||||
|
} else if ((/^invalid-list-model:/).test(response)) {
|
||||||
|
message = `<strong>${response.replace(/'/g, "").replace(/^[^:]*:/, "").replace(/,([^,]*)$/, "</strong> and <strong>$1").replace(/,/g, "</strong>, <strong>")}</strong> is not a valid list`;
|
||||||
} else {
|
} else {
|
||||||
message = `Failed to <strong>${operation}</strong> record`;
|
message = `Failed to <strong>${operation}</strong> record`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -917,6 +917,12 @@ form {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
margin-bottom: pxrem(15);
|
margin-bottom: pxrem(15);
|
||||||
padding-bottom: pxrem(15);
|
padding-bottom: pxrem(15);
|
||||||
|
@ -927,17 +933,37 @@ form {
|
||||||
}
|
}
|
||||||
|
|
||||||
&-items-row {
|
&-items-row {
|
||||||
|
position: relative;
|
||||||
margin-bottom: pxrem(10);
|
margin-bottom: pxrem(10);
|
||||||
display: flex;
|
padding: pxrem(8) pxrem(8) pxrem(8) pxrem(33);
|
||||||
align-items: center;
|
border-radius: pxrem(6);
|
||||||
|
background-color: darken($c-input-bg, 2%);
|
||||||
|
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
display: flex;
|
||||||
|
padding: pxrem(5) pxrem(5) pxrem(5) pxrem(30);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
@include media-breakpoint-down(lg) {
|
||||||
|
margin-bottom: pxrem(15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sort-icon {
|
.sort-icon {
|
||||||
margin-right: pxrem(10);
|
position: absolute;
|
||||||
|
top: pxrem(11);
|
||||||
|
left: pxrem(10);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 100ms;
|
transition: opacity 100ms;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
top: pxrem(9);
|
||||||
|
}
|
||||||
|
|
||||||
&-inner {
|
&-inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: pxrem(2);
|
top: pxrem(2);
|
||||||
|
@ -968,17 +994,29 @@ form {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-input, &-button {
|
&-content, &-button {
|
||||||
&:not(:first-child) {
|
@include media-breakpoint-down(lg) {
|
||||||
margin-left: pxrem(5);
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-right: pxrem(5);
|
@include media-breakpoint-down(lg) {
|
||||||
|
margin-bottom: pxrem(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
margin-right: pxrem(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
margin-left: pxrem(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-input {
|
&-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.wide {
|
&.wide {
|
||||||
|
@ -986,29 +1024,52 @@ form {
|
||||||
}
|
}
|
||||||
|
|
||||||
&-inner {
|
&-inner {
|
||||||
margin-bottom: 0px;
|
$row-height: pxrem(36);
|
||||||
}
|
display: flex;
|
||||||
|
height: $row-height;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&-overlay {
|
.list-input {
|
||||||
overflow: hidden;
|
margin-bottom: 0px;
|
||||||
position: absolute;
|
height: auto;
|
||||||
top: 0px;
|
}
|
||||||
left: 0px;
|
|
||||||
width: 100%;
|
.image-link {
|
||||||
height: 100%;
|
margin-right: pxrem(10);
|
||||||
padding: pxrem(5) pxrem(8);
|
display: block;
|
||||||
background-color: $c-input-bg;
|
width: pxrem(50);
|
||||||
white-space: nowrap;
|
height: $row-height;
|
||||||
text-overflow: ellipsis;
|
background-color: $c-input-bg;
|
||||||
pointer-events: none;
|
|
||||||
|
&:not(.active) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-button {
|
&-buttons {
|
||||||
min-width: pxrem(70);
|
@include media-breakpoint-up(lg) {
|
||||||
height: $label-height;
|
display: flex;
|
||||||
border: 1px solid fade-out($c-text, 0.75);
|
flex-grow: 1;
|
||||||
border-radius: pxrem(4);
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-delete {
|
||||||
|
min-width: pxrem(70);
|
||||||
|
height: $label-height;
|
||||||
|
border: 1px solid fade-out($c-text, 0.75);
|
||||||
|
border-radius: pxrem(4);
|
||||||
|
background-color: $c-dashboard-delete;
|
||||||
|
color: $c-text-light;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,6 +1077,11 @@ form {
|
||||||
height: $label-height;
|
height: $label-height;
|
||||||
border: 1px solid fade-out($c-text, 0.75);
|
border: 1px solid fade-out($c-text, 0.75);
|
||||||
border-radius: pxrem(4);
|
border-radius: pxrem(4);
|
||||||
|
|
||||||
|
@include media-breakpoint-down(lg) {
|
||||||
|
margin-top: pxrem(15);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1101,6 +1167,7 @@ form {
|
||||||
padding: pxrem(5) pxrem(10);
|
padding: pxrem(5) pxrem(10);
|
||||||
border-radius: pxrem(5);
|
border-radius: pxrem(5);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
text-decoration: none;
|
||||||
transition: background-color 150ms;
|
transition: background-color 150ms;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
@ -1153,6 +1220,10 @@ form {
|
||||||
margin-top: pxrem(10);
|
margin-top: pxrem(10);
|
||||||
margin-bottom: pxrem(20);
|
margin-bottom: pxrem(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
@php
|
@php
|
||||||
$value = $item !== null ? $item[$column['name']] : '';
|
$value = $item !== null ? $item[$column['name']] : '';
|
||||||
$type = $id == 'new' && array_key_exists('type-new', $column) ? $column['type-new'] : $column['type'];
|
$type = $id == 'new' && array_key_exists('type-new', $column) ? $column['type-new'] : $column['type'];
|
||||||
$ext = array_key_exists('ext', $column) ? $column['ext'] : 'jpg';
|
$ext = array_key_exists('ext', $column) ? $column['ext'] : $default_img_ext;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@if($type == 'hidden')
|
@if($type == 'hidden')
|
||||||
|
@ -72,6 +72,13 @@
|
||||||
@endphp
|
@endphp
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if(gettype($select_title))
|
||||||
|
@php
|
||||||
|
$select_value = $select_value ? 1 : 0;
|
||||||
|
$select_title = $select_title ? 'true' : 'false';
|
||||||
|
@endphp
|
||||||
|
@endif
|
||||||
|
|
||||||
@if($select_value === $value)
|
@if($select_value === $value)
|
||||||
<option value="{{ $select_value }}" selected="selected">{{ $select_title }}</option>
|
<option value="{{ $select_value }}" selected="selected">{{ $select_title }}</option>
|
||||||
@else
|
@else
|
||||||
|
@ -80,9 +87,14 @@
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
@elseif($type == 'list')
|
@elseif($type == 'list')
|
||||||
|
@php
|
||||||
|
$list_model = App\Dashboard::getModel($value['model']);
|
||||||
|
$list_columns = $list_model::$dashboard_columns;
|
||||||
|
@endphp
|
||||||
|
|
||||||
<div class="list" id="{{ $column['name'] }}">
|
<div class="list" id="{{ $column['name'] }}">
|
||||||
<div class="list-template">
|
<div class="list-template">
|
||||||
<div class="list-items-row">
|
<div class="list-items-row" data-id="new">
|
||||||
<div class="sort-icon" title="Click and drag to reorder">
|
<div class="sort-icon" title="Click and drag to reorder">
|
||||||
<div class="sort-icon-inner">
|
<div class="sort-icon-inner">
|
||||||
<div class="sort-icon-inner-bar"></div>
|
<div class="sort-icon-inner-bar"></div>
|
||||||
|
@ -91,22 +103,46 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@foreach($column['columns'] as $list_column)
|
@foreach($list_columns as $list_column)
|
||||||
<div class="list-items-row-input {{ count($column['columns']) == 1 ? 'wide' : '' }}">
|
<div class="list-items-row-content {{ count($list_columns) == 1 ? 'wide' : '' }}">
|
||||||
<input class="list-items-row-input-inner" data-column="{{ $list_column }}" placeholder="{{ $list_column }}" />
|
<div class="list-items-row-content-inner" data-column="{{ $list_column['name'] }}" data-type="{{ $list_column['type'] }}">
|
||||||
|
@if($list_column['type'] == 'string')
|
||||||
|
<input class="list-input" placeholder="{{ $list_column['name'] }}" />
|
||||||
|
@elseif($list_column['type'] == 'image')
|
||||||
|
<a class="image-link" href="" target="_blank"><img class="image-preview" src="" /></a>
|
||||||
|
<input class="list-input image-upload" type="file" data-column="{{ $list_column['name'] }}" data-model="{{ $value['model'] }}" />
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
<button class="list-items-row-button" type="button">Delete</button>
|
<div class="list-items-row-buttons">
|
||||||
|
<button class="list-items-row-buttons-delete" type="button">Delete</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="list-data">
|
<div class="list-data">
|
||||||
@if($id != 'new')
|
@if($id != 'new')
|
||||||
@foreach($value as $row)
|
@foreach($value['list'] as $row)
|
||||||
<div class="list-data-row">
|
<div class="list-data-row" data-id="{{ $row['id'] }}">
|
||||||
@foreach($column['columns'] as $list_column)
|
@foreach($list_columns as $list_column)
|
||||||
<div class="list-data-row-item" data-column="{{ $list_column }}" data-value="{{ $row[$list_column] }}"></div>
|
@if($list_column['type'] == 'string')
|
||||||
|
@php
|
||||||
|
$list_column_value = $row[$list_column['name']]
|
||||||
|
@endphp
|
||||||
|
@elseif($list_column['type'] == 'image')
|
||||||
|
@php
|
||||||
|
$list_column_item = $list_model::find($row['id']);
|
||||||
|
$list_column_image_ext = array_key_exists('ext', $list_column) ? $list_column['ext'] : $default_img_ext;
|
||||||
|
$list_column_image_path = $list_model->getUploadsPath('image') . $row['id'] . '-' . $list_column['name'] . '.' . $list_column_image_ext;
|
||||||
|
$list_column_value = file_exists(public_path($list_column_image_path)) ? $list_column_image_path . '?version=' . $list_column_item->timestamp() : '';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
{{ $list_column_image_path }}
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="list-data-row-item" data-type="{{ $list_column['type'] }}" data-column="{{ $list_column['name'] }}" data-value="{{ $list_column_value }}"></div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
@ -121,14 +157,14 @@
|
||||||
$current_image = "/uploads/$model/img/$id-" . $column['name'] . '.' . $ext;
|
$current_image = "/uploads/$model/img/$id-" . $column['name'] . '.' . $ext;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<input class="image-upload" type="file" name="{{ $column['name'] }}" id="{{ $column['name'] }}" />
|
<input class="image-upload" type="file" data-column="{{ $column['name'] }}" data-model="{{ $model }}" data-id="{{ $id }}" />
|
||||||
|
|
||||||
@if(file_exists(base_path() . '/public' . $current_image))
|
@if(file_exists(base_path() . '/public' . $current_image))
|
||||||
<div id="current-image-{{ $column['name'] }}">
|
<div id="current-image-{{ $column['name'] }}">
|
||||||
<img class="current-image" src="{{ $current_image }}?version={{ $item->timestamp() }}" />
|
<img class="current-image" src="{{ $current_image }}?version={{ $item->timestamp() }}" />
|
||||||
|
|
||||||
@if(array_key_exists('delete', $column) && $column['delete'])
|
@if(array_key_exists('delete', $column) && $column['delete'])
|
||||||
<span class="edit-button delete image" data-name="{{ $column['name'] }}">
|
<span class="edit-button delete image" data-column="{{ $column['name'] }}" data-model="{{ $model }}" data-id="{{ $id }}">
|
||||||
Delete Image
|
Delete Image
|
||||||
</span>
|
</span>
|
||||||
@endif
|
@endif
|
||||||
|
@ -139,14 +175,14 @@
|
||||||
$current_file = "/uploads/$model/files/$id-" . $column['name'] . '.' . $column['ext'];
|
$current_file = "/uploads/$model/files/$id-" . $column['name'] . '.' . $column['ext'];
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<input class="file-upload" type="file" name="{{ $column['name'] }}" id="{{ $column['name'] }}" />
|
<input class="file-upload" type="file" data-column="{{ $column['name'] }}" data-model="{{ $model }}" data-id="{{ $id }}" />
|
||||||
|
|
||||||
@if(file_exists(base_path() . '/public' . $current_file))
|
@if(file_exists(base_path() . '/public' . $current_file))
|
||||||
<div id="current-file-{{ $column['name'] }}">
|
<div id="current-file-{{ $column['name'] }}">
|
||||||
<a class="edit-button view" href="{{ $current_file }}?version={{ $item->timestamp() }}" target="_blank">View {{ strtoupper($column['ext']) }}</a>
|
<a class="edit-button view" href="{{ $current_file }}?version={{ $item->timestamp() }}" target="_blank">View {{ strtoupper($column['ext']) }}</a>
|
||||||
|
|
||||||
@if(array_key_exists('delete', $column) && $column['delete'])
|
@if(array_key_exists('delete', $column) && $column['delete'])
|
||||||
<span class="edit-button delete file" data-name="{{ $column['name'] }}">
|
<span class="edit-button delete file" data-column="{{ $column['name'] }}" data-model="{{ $model }}" data-id="{{ $id }}">
|
||||||
Delete {{ strtoupper($column['ext']) }}
|
Delete {{ strtoupper($column['ext']) }}
|
||||||
</span>
|
</span>
|
||||||
@endif
|
@endif
|
||||||
|
|
Loading…
Reference in a new issue