add table viewing page

pull/26/head
Ondřej Hruška 6 years ago
parent 26488e5883
commit e01c63cfa2
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 28
      app/Http/Controllers/TableController.php
  2. 2
      app/Models/Table.php
  3. 8
      app/Providers/AppServiceProvider.php
  4. 7
      app/Utils/Column.php
  5. 16
      app/View/Widget.php
  6. 18
      resources/assets/js/app.js
  7. 102
      resources/assets/js/url-slug.js
  8. 4
      resources/views/home.blade.php
  9. 6
      resources/views/table/create.blade.php
  10. 39
      resources/views/table/view.blade.php
  11. 3
      routes/public.php

@ -12,6 +12,20 @@ use MightyPork\Exceptions\NotApplicableException;
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
*
@ -20,8 +34,8 @@ class TableController extends Controller
public function create()
{
$exampleColumns =
"latin,string,Latin Name\n".
"common,string,Common Name\n".
"latin,string,Latin Name\n" .
"common,string,Common Name\n" .
"lifespan,int,Lifespan (years)";
$exampleData =
@ -82,11 +96,12 @@ class TableController extends Controller
$rowTable = array_map('str_getcsv', explode("\n", $request->get('data')));
// Preparing data to insert into the Rows table
$rowsData = null;
try {
$rowsData = array_map(function ($row) use ($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 = [];
foreach ($row as $i => $val) {
@ -98,7 +113,7 @@ class TableController extends Controller
'refs' => 1,
];
}, $rowTable);
}catch (\Exception $e) {
} catch (\Exception $e) {
return $this->backWithErrors(['columns' => $e->getMessage()]);
}
@ -118,6 +133,11 @@ class TableController extends Controller
'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);
// Now we create rows, a revision pointing to them, and the table using it.

@ -79,7 +79,7 @@ class Table extends Model
/** Active revision */
public function activeRevision()
{
return $this->hasOne(Revision::class, 'revision_id');
return $this->belongsTo(Revision::class, 'revision_id');
}
/** Proposals submitted to this table */

@ -2,6 +2,7 @@
namespace App\Providers;
use App\Models\User;
use App\View\WidgetFactory;
use Illuminate\Support\ServiceProvider;
@ -17,6 +18,13 @@ class AppServiceProvider extends ServiceProvider
if($this->app->environment('production')) {
\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 $type;
public static function columnsFromJson($columns)
{
return array_map(function ($x) {
return new Column($x);
}, $columns);
}
public function __get($name)
{
if (property_exists($this, $name)) {

@ -53,18 +53,32 @@ class Widget
$this->$method = $arg;
}
else {
$this->attributesArray[$method] = $arg;
$this->attributesArray[kebab_case($method)] = $arg;
}
return $this;
}
/** Explicitly set a CSS rule */
public function css($prop, $val)
{
$this->styleArray[$prop] = $val;
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) */
public function layout($labelCols, $fieldCols)
{

@ -6,6 +6,7 @@
*/
require('./bootstrap');
let url_slug = require('./url-slug')
$(function () {
$('[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');

@ -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>
@else
@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
{{ $tables->links() }}
@endif

@ -9,13 +9,13 @@
@csrf
<div class="col-md-8">
{!! Widget::text('title', 'Title')->autoAlias('name', '-')
->help('Unique among your tables') !!}
{!! Widget::text('name', 'Name')->value('molluscs-'.uniqid())
->help('Unique among your tables, and part of the URL; only letters, digits and
some symbols are allowed.') !!}
{!! Widget::text('title', 'Title')
->help('Unique among your tables') !!}
{!! Widget::textarea('description', 'Description')->height('8em')
->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.') !!}

@ -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');
});
// Table resource
Route::get('{user}/{table}', 'TableController@view')->name('table.view');

Loading…
Cancel
Save