add table viewing page
This commit is contained in:
@@ -12,6 +12,20 @@ use MightyPork\Exceptions\NotApplicableException;
|
|||||||
|
|
||||||
class TableController extends Controller
|
class TableController extends Controller
|
||||||
{
|
{
|
||||||
|
public function view(User $user, string $table)
|
||||||
|
{
|
||||||
|
/** @var Table $tableModel */
|
||||||
|
$tableModel = $user->tables()->where('name', $table)->first();
|
||||||
|
$revision = $tableModel->activeRevision;
|
||||||
|
|
||||||
|
return view('table.view', [
|
||||||
|
'table' => $tableModel,
|
||||||
|
'revision' => $revision,
|
||||||
|
'columns' => Column::columnsFromJson(json_decode($revision->columns)),
|
||||||
|
'rows' => $revision->rows,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SHow a form for creating a new table
|
* SHow a form for creating a new table
|
||||||
*
|
*
|
||||||
@@ -20,8 +34,8 @@ class TableController extends Controller
|
|||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
$exampleColumns =
|
$exampleColumns =
|
||||||
"latin,string,Latin Name\n".
|
"latin,string,Latin Name\n" .
|
||||||
"common,string,Common Name\n".
|
"common,string,Common Name\n" .
|
||||||
"lifespan,int,Lifespan (years)";
|
"lifespan,int,Lifespan (years)";
|
||||||
|
|
||||||
$exampleData =
|
$exampleData =
|
||||||
@@ -82,11 +96,12 @@ class TableController extends Controller
|
|||||||
|
|
||||||
$rowTable = array_map('str_getcsv', explode("\n", $request->get('data')));
|
$rowTable = array_map('str_getcsv', explode("\n", $request->get('data')));
|
||||||
|
|
||||||
|
// Preparing data to insert into the Rows table
|
||||||
$rowsData = null;
|
$rowsData = null;
|
||||||
try {
|
try {
|
||||||
$rowsData = array_map(function ($row) use ($columns) {
|
$rowsData = array_map(function ($row) use ($columns) {
|
||||||
if (count($row) != count($columns)) {
|
if (count($row) != count($columns)) {
|
||||||
throw new NotApplicableException("All rows must have ".count($columns)." fields.");
|
throw new NotApplicableException("All rows must have " . count($columns) . " fields.");
|
||||||
}
|
}
|
||||||
$parsed = [];
|
$parsed = [];
|
||||||
foreach ($row as $i => $val) {
|
foreach ($row as $i => $val) {
|
||||||
@@ -98,7 +113,7 @@ class TableController extends Controller
|
|||||||
'refs' => 1,
|
'refs' => 1,
|
||||||
];
|
];
|
||||||
}, $rowTable);
|
}, $rowTable);
|
||||||
}catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $this->backWithErrors(['columns' => $e->getMessage()]);
|
return $this->backWithErrors(['columns' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +133,11 @@ class TableController extends Controller
|
|||||||
'origin' => $request->get('origin'),
|
'origin' => $request->get('origin'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Attach the revision to the table, set as current
|
||||||
|
$table->revisions()->attach($revision);
|
||||||
|
$table->activeRevision()->associate($revision);
|
||||||
|
|
||||||
|
// Spawn the rows, linked to the revision
|
||||||
$revision->rows()->createMany($rowsData);
|
$revision->rows()->createMany($rowsData);
|
||||||
|
|
||||||
// Now we create rows, a revision pointing to them, and the table using it.
|
// Now we create rows, a revision pointing to them, and the table using it.
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class Table extends Model
|
|||||||
/** Active revision */
|
/** Active revision */
|
||||||
public function activeRevision()
|
public function activeRevision()
|
||||||
{
|
{
|
||||||
return $this->hasOne(Revision::class, 'revision_id');
|
return $this->belongsTo(Revision::class, 'revision_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Proposals submitted to this table */
|
/** Proposals submitted to this table */
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use App\View\WidgetFactory;
|
use App\View\WidgetFactory;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
@@ -17,6 +18,13 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
if($this->app->environment('production')) {
|
if($this->app->environment('production')) {
|
||||||
\URL::forceScheme('https');
|
\URL::forceScheme('https');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
\Route::bind('user', function ($value) {
|
||||||
|
$u = User::where('name', $value)->first();
|
||||||
|
// it may also be the _id directly
|
||||||
|
if (!$u) $u = User::find($value);
|
||||||
|
return $u;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,6 +25,13 @@ class Column implements JsonSerializable
|
|||||||
private $title;
|
private $title;
|
||||||
private $type;
|
private $type;
|
||||||
|
|
||||||
|
public static function columnsFromJson($columns)
|
||||||
|
{
|
||||||
|
return array_map(function ($x) {
|
||||||
|
return new Column($x);
|
||||||
|
}, $columns);
|
||||||
|
}
|
||||||
|
|
||||||
public function __get($name)
|
public function __get($name)
|
||||||
{
|
{
|
||||||
if (property_exists($this, $name)) {
|
if (property_exists($this, $name)) {
|
||||||
|
|||||||
+15
-1
@@ -53,18 +53,32 @@ class Widget
|
|||||||
$this->$method = $arg;
|
$this->$method = $arg;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->attributesArray[$method] = $arg;
|
$this->attributesArray[kebab_case($method)] = $arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Explicitly set a CSS rule */
|
||||||
public function css($prop, $val)
|
public function css($prop, $val)
|
||||||
{
|
{
|
||||||
$this->styleArray[$prop] = $val;
|
$this->styleArray[$prop] = $val;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Explicitly set an attribute */
|
||||||
|
public function attr($attr, $val)
|
||||||
|
{
|
||||||
|
$this->attributesArray[$attr] = $val;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Configure a text field for automatic alias generation */
|
||||||
|
public function autoAlias($targetName, $delimiter='_')
|
||||||
|
{
|
||||||
|
return $this->attr('data-autoalias', $targetName)->attr('data-aa-delimiter', $delimiter);
|
||||||
|
}
|
||||||
|
|
||||||
/** Apply a given layout (bootstrap grid, 12 cols total) */
|
/** Apply a given layout (bootstrap grid, 12 cols total) */
|
||||||
public function layout($labelCols, $fieldCols)
|
public function layout($labelCols, $fieldCols)
|
||||||
{
|
{
|
||||||
|
|||||||
Vendored
+18
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
require('./bootstrap');
|
require('./bootstrap');
|
||||||
|
let url_slug = require('./url-slug')
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
$('[data-toggle="tooltip"]').tooltip({
|
$('[data-toggle="tooltip"]').tooltip({
|
||||||
@@ -13,6 +14,23 @@ $(function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$(document).on('input keypress paste keyup', 'input[data-autoalias]', function () {
|
||||||
|
const $this = $(this)
|
||||||
|
const target_name = $this.data('autoalias')
|
||||||
|
const delimiter = $this.data('aa-delimiter') || '_'
|
||||||
|
|
||||||
|
const new_alias = url_slug($this.val(), {'delimiter': delimiter})
|
||||||
|
|
||||||
|
const $target = $(`input[name="${target_name}"]`)
|
||||||
|
const lastset = $target.data('aa-last-set-val')
|
||||||
|
|
||||||
|
// 1. pick up, or 2. continue
|
||||||
|
if (new_alias === $target.val() || lastset === $target.val()) {
|
||||||
|
$target.val(new_alias)
|
||||||
|
$target.data('aa-last-set-val', new_alias)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// window.Vue = require('vue');
|
// window.Vue = require('vue');
|
||||||
|
|||||||
Vendored
+102
@@ -0,0 +1,102 @@
|
|||||||
|
// Source & more langs: https://gist.github.com/sgmurphy/3095196
|
||||||
|
|
||||||
|
|
||||||
|
const char_map = {
|
||||||
|
// Latin
|
||||||
|
'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 'C',
|
||||||
|
'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 'Ï': 'I',
|
||||||
|
'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 'O', 'Ő': 'O',
|
||||||
|
'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U', 'Ý': 'Y', 'Þ': 'TH',
|
||||||
|
'ß': 'ss',
|
||||||
|
'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c',
|
||||||
|
'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i',
|
||||||
|
'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o',
|
||||||
|
'ø': 'o', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th',
|
||||||
|
'ÿ': 'y',
|
||||||
|
|
||||||
|
// Latin symbols
|
||||||
|
'©': '(c)',
|
||||||
|
|
||||||
|
// Czech
|
||||||
|
'Č': 'C', 'Ď': 'D', 'Ě': 'E', 'Ň': 'N', 'Ř': 'R', 'Š': 'S', 'Ť': 'T', 'Ů': 'U',
|
||||||
|
'Ž': 'Z',
|
||||||
|
'č': 'c', 'ď': 'd', 'ě': 'e', 'ň': 'n', 'ř': 'r', 'š': 's', 'ť': 't', 'ů': 'u',
|
||||||
|
'ž': 'z',
|
||||||
|
|
||||||
|
// Polish
|
||||||
|
'Ą': 'A', 'Ć': 'C', 'Ę': 'e', 'Ł': 'L', 'Ń': 'N', 'Ś': 'S', 'Ź': 'Z',
|
||||||
|
'Ż': 'Z',
|
||||||
|
'ą': 'a', 'ć': 'c', 'ę': 'e', 'ł': 'l', 'ń': 'n', 'ś': 's', 'ź': 'z',
|
||||||
|
'ż': 'z',
|
||||||
|
}
|
||||||
|
|
||||||
|
const alnum = new RegExp('[^a-z0-9]+', 'ig')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a web friendly URL slug from a string.
|
||||||
|
*
|
||||||
|
* Although supported, transliteration is discouraged because
|
||||||
|
* 1) most web browsers support UTF-8 characters in URLs
|
||||||
|
* 2) transliteration causes a loss of information
|
||||||
|
*
|
||||||
|
* @author Sean Murphy <sean@iamseanmurphy.com>
|
||||||
|
* @copyright Copyright 2012 Sean Murphy. All rights reserved.
|
||||||
|
* @license http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @param {string} s
|
||||||
|
* @param {object} opt
|
||||||
|
*/
|
||||||
|
function url_slug (s, opt) {
|
||||||
|
let k
|
||||||
|
|
||||||
|
s = String(s)
|
||||||
|
opt = Object(opt)
|
||||||
|
|
||||||
|
const defaults = {
|
||||||
|
'delimiter': '-',
|
||||||
|
'limit': undefined,
|
||||||
|
'lowercase': true,
|
||||||
|
'replacements': {},
|
||||||
|
'transliterate': true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge options
|
||||||
|
for (k in defaults) {
|
||||||
|
if (defaults.hasOwnProperty(k) && !opt.hasOwnProperty(k)) {
|
||||||
|
opt[k] = defaults[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make custom replacements
|
||||||
|
for (k in opt.replacements) {
|
||||||
|
if (opt.replacements.hasOwnProperty(k)) {
|
||||||
|
s = s.replace(new RegExp(k, 'g'), opt.replacements[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transliterate characters to ASCII
|
||||||
|
if (opt.transliterate) {
|
||||||
|
for (k in char_map) {
|
||||||
|
if (char_map.hasOwnProperty(k)) {
|
||||||
|
s = s.replace(new RegExp(k, 'g'), char_map[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace non-alphanumeric characters with our delimiter
|
||||||
|
s = s.replace(alnum, opt.delimiter)
|
||||||
|
|
||||||
|
// Remove duplicate delimiters
|
||||||
|
s = s.replace(new RegExp('[' + opt.delimiter + ']{2,}', 'g'), opt.delimiter)
|
||||||
|
|
||||||
|
// Truncate slug to max. characters
|
||||||
|
s = s.substring(0, opt.limit)
|
||||||
|
|
||||||
|
// Remove delimiter from ends
|
||||||
|
s = s.replace(new RegExp('(^' + opt.delimiter + '|' + opt.delimiter + '$)', 'g'), '')
|
||||||
|
|
||||||
|
return opt.lowercase ? s.toLowerCase() : s
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = url_slug;
|
||||||
@@ -38,7 +38,9 @@
|
|||||||
<span class="list-group-item">No tables yet.</span>
|
<span class="list-group-item">No tables yet.</span>
|
||||||
@else
|
@else
|
||||||
@foreach($tables as $table)
|
@foreach($tables as $table)
|
||||||
<a class="list-group-item list-group-item-action" href="">{{ $table->title }}</a>
|
<a class="list-group-item list-group-item-action" href="{{route('table.view', ['user' => Auth::user()->name, 'table' => $table->name])}}">{{
|
||||||
|
$table->title
|
||||||
|
}}</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
{{ $tables->links() }}
|
{{ $tables->links() }}
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
|
|
||||||
@csrf
|
@csrf
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
|
{!! Widget::text('title', 'Title')->autoAlias('name', '-')
|
||||||
|
->help('Unique among your tables') !!}
|
||||||
|
|
||||||
{!! Widget::text('name', 'Name')->value('molluscs-'.uniqid())
|
{!! Widget::text('name', 'Name')->value('molluscs-'.uniqid())
|
||||||
->help('Unique among your tables, and part of the URL; only letters, digits and
|
->help('Unique among your tables, and part of the URL; only letters, digits and
|
||||||
some symbols are allowed.') !!}
|
some symbols are allowed.') !!}
|
||||||
|
|
||||||
{!! Widget::text('title', 'Title')
|
|
||||||
->help('Unique among your tables') !!}
|
|
||||||
|
|
||||||
{!! Widget::textarea('description', 'Description')->height('8em')
|
{!! Widget::textarea('description', 'Description')->height('8em')
|
||||||
->help('Description what data is in the table. Please use the dedicated
|
->help('Description what data is in the table. Please use the dedicated
|
||||||
fields for License and data source. URLs in a full format will be clickable.') !!}
|
fields for License and data source. URLs in a full format will be clickable.') !!}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@php
|
||||||
|
/** @var \App\Models\Table $table */
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<h2>{{ $table->title }}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
|
||||||
|
<div class="col-md-8">
|
||||||
|
<table class="table table-hover table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
@foreach($columns as $col)
|
||||||
|
<th>{{ $col->title }}</th>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($rows as $row)
|
||||||
|
<tr>
|
||||||
|
<td>#{{ $row->id }}</td>
|
||||||
|
@php($rdata = json_decode($row['data'], true))
|
||||||
|
@foreach($columns as $col)
|
||||||
|
<td data-id="{{ $row->id }}">{{ $rdata[$col->name] }}</td>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -17,3 +17,6 @@ Route::get('/', function () {
|
|||||||
}
|
}
|
||||||
return view('welcome');
|
return view('welcome');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Table resource
|
||||||
|
Route::get('{user}/{table}', 'TableController@view')->name('table.view');
|
||||||
|
|||||||
Reference in New Issue
Block a user