Note tab added

pull/35/head
Ondřej Hruška 6 years ago
parent c952798e66
commit b1d1fa4880
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 14
      app/Http/Controllers/TableEditController.php
  2. 16
      app/Tables/Changeset.php
  3. 2
      app/helpers.php
  4. 221
      resources/assets/js/components/ColumnEditor.vue
  5. 2
      resources/assets/js/components/RowsEditor.vue
  6. 12
      resources/assets/sass/_helpers.scss
  7. 8
      resources/views/table/propose/layout.blade.php
  8. 117
      resources/views/table/propose/review.blade.php

@ -141,6 +141,15 @@ class TableEditController extends Controller
]); ]);
} }
/** @noinspection PhpUnusedPrivateMethodInspection */
private function tabReview(Changeset $changeset)
{
return view('table.propose.review', [
'changeset' => $changeset,
'table' => $changeset->table,
]);
}
#endregion #endregion
/** /**
@ -255,6 +264,11 @@ class TableEditController extends Controller
$changeset->setColOrder($input->order); $changeset->setColOrder($input->order);
break; break;
case 'col.reset-sort':
$changeset->columnOrder = [];
$resp = $changeset->fetchAndTransformColumns();
break;
default: default:
$resp = "Bad Action"; $resp = "Bad Action";
$code = 400; $code = 400;

@ -485,6 +485,8 @@ class Changeset
else { else {
$this->removedColumns[] = $id; $this->removedColumns[] = $id;
} }
$this->clearColumnOrderIfUnchanged();
} }
public function columnRestore(string $id) public function columnRestore(string $id)
@ -610,6 +612,7 @@ class Changeset
$missing = array_diff($ids, $order); $missing = array_diff($ids, $order);
$this->columnOrder = array_merge($order, $missing); $this->columnOrder = array_merge($order, $missing);
$this->clearColumnOrderIfUnchanged();
} }
public function removeEmptyNewRows() public function removeEmptyNewRows()
@ -634,4 +637,17 @@ class Changeset
return false; return false;
}); });
} }
public function clearColumnOrderIfUnchanged()
{
$expected = collect($this->revision->columns)
->pluck('id')
->diff($this->removedColumns)
->merge(collect($this->newColumns)->pluck('id'))
->all();
if ($expected == $this->columnOrder) {
$this->columnOrder = [];
}
}
} }

@ -9,7 +9,7 @@ const VALI_LINE = 'string|max:255';
const FEATURE_FORKS = false; const FEATURE_FORKS = false;
const FEATURE_FAVES = false; const FEATURE_FAVES = false;
const FEATURE_TABLE_COMMENTS = false; const FEATURE_TABLE_COMMENTS = false;
const FEATURE_PROPOSALS = false; const FEATURE_PROPOSALS = true;
// global helpers // global helpers
function authed() { function authed() {

@ -4,108 +4,117 @@ Complex animated column editor for the table edit page
<template> <template>
<div> <div>
<input type="hidden" :name="name" :value="JSON.stringify(columns)" v-if="newTable"> <input type="hidden" :name="name" :value="JSON.stringify(columns)" v-if="!newTable">
<table :class="[
{'table': !newTable}, <div :class="newTable ? ['col-md-12', 'mt-3'] : []">
{'new-table': newTable}, <table :class="[
{'mt-3': !newTable}, {'table': !newTable},
'table-narrow', 'table-sm', 'table-fixed', 'td-va-middle' {'new-table': newTable},
]"> {'mt-3': !newTable},
<thead> 'table-narrow', 'table-sm', 'table-fixed', 'td-va-middle'
<tr> ]">
<th v-if="sortable"></th> <thead>
<th :style="tdWidthStyle('name')">Name</th> <tr>
<th :style="tdWidthStyle('type')">Type</th> <th v-if="sortable"></th>
<th :style="tdWidthStyle('title')">Title</th> <th :style="tdWidthStyle('name')">Name</th>
<th></th> <th :style="tdWidthStyle('type')">Type</th>
</tr> <th :style="tdWidthStyle('title')">Title</th>
</thead> <th>
<transition-group name="col-list" tag="tbody" ref="col-list"> <a href="" type="button" v-if="!newTable"
<tr v-for="(col, i) in columns" :key="col.id" :ref="`col${i}`" @click.prevent="resetOrder()"
:class="{ class="text-danger no-decoration">
dragging: col._dragging, Reset Order
'text-success': col._new, </a>
remove: col._remove </th>
}"> </tr>
<td v-if="sortable"> </thead>
<span class="btn-group"> <transition-group name="col-list" tag="tbody" ref="col-list">
<button type="button" class="btn btn-outline-secondary drag-btn" <tr v-for="(col, i) in columns" :key="col.id" :ref="`col${i}`"
@keyup.up="move(i, -1)" :class="{
@keyup.down="move(i, 1)" dragging: col._dragging,
:ref="`col${i}-sort`" 'text-success': col._new,
:style="{visibility: (columns.length > 1) ? 'visible' : 'hidden'}" remove: col._remove
@mousedown="beginDrag(i, $event)"> }">
<v-icon class="fa-bars" alt="Drag" /> <td v-if="sortable">
</button><!-- <span class="btn-group">
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i==0}]" v-if="manualSort" <button type="button" class="btn btn-outline-secondary drag-btn"
@click.prevent="move(i, -1)"> @keyup.up="move(i, -1)"
<v-icon class="fa-chevron-up" alt="Move Up" /> @keyup.down="move(i, 1)"
</button><!-- :ref="`col${i}-sort`"
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i == (columns.length-1)}]" v-if="manualSort" :style="{visibility: (columns.length > 1) ? 'visible' : 'hidden'}"
@click.prevent="move(i, 1)"> @mousedown="beginDrag(i, $event)">
<v-icon class="fa-chevron-down" alt="Move Down" /> <v-icon class="fa-bars" alt="Drag" />
</button> </button><!--
</span> --><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i==0}]" v-if="manualSort"
</td> @click.prevent="move(i, -1)">
<v-icon class="fa-chevron-up" alt="Move Up" />
<template v-if="col._editing || newTable"> </button><!--
<!-- Editable cells --> --><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i == (columns.length-1)}]" v-if="manualSort"
<td :style="tdWidthStyle('name')"> @click.prevent="move(i, 1)">
<input v-model="col.name" <v-icon class="fa-chevron-down" alt="Move Down" />
class="form-control" </button>
type="text"> </span>
</td>
<td :style="tdWidthStyle('type')">
<select v-model="col.type"
class="form-control custom-select">
<option v-for="t in colTypes" :value="t">{{t}}</option>
</select>
</td> </td>
<td :style="tdWidthStyle('title')"> <template v-if="col._editing || newTable">
<input v-model="col.title" <!-- Editable cells -->
class="form-control" <td :style="tdWidthStyle('name')">
type="text"> <input v-model="col.name"
</td> class="form-control"
</template> type="text">
</td>
<template v-else>
<!-- Value fields --> <td :style="tdWidthStyle('type')">
<td v-for='cell in ["name", "type", "title"]'> <select v-model="col.type"
<span class="text-danger strike" title="Original value" v-if="isChanged(col, cell)">{{col._orig[cell]}}</span> class="form-control custom-select">
<span>{{ col[cell] }}</span> <option v-for="t in colTypes" :value="t">{{t}}</option>
<v-icon v-if="isChanged(col, cell)" </select>
@click="revertCell(col, cell)" </td>
class="fa-undo text-danger pointer"
alt="Revert Change" /> <td :style="tdWidthStyle('title')">
<input v-model="col.title"
class="form-control"
type="text">
</td>
</template>
<template v-else>
<!-- Value fields -->
<td v-for='cell in ["name", "type", "title"]'>
<span class="text-danger strike" title="Original value" v-if="isChanged(col, cell)">{{col._orig[cell]}}</span>
<span>{{ col[cell] }}</span>
<v-icon v-if="isChanged(col, cell)"
@click="revertCell(col, cell)"
class="fa-undo text-danger pointer"
alt="Revert Change" />
</td>
</template>
<td class="text-nowrap"><!--
Save button
--><a href="" :class="['mr-1', 'btn', 'btn-outline-secondary', {'disabled': col._remove}]"
v-if="!newTable"
@click.prevent="toggleColEditing(col)">
<v-icon v-if="col._editing" class="fa-save" alt="Save" />
<v-icon v-else class="fa-pencil" alt="Edit" />
</a><!--
Delete button
--><button type="button" :class="delBtnClass(col)"
@click.prevent="toggleColDelete(col)">
<v-icon v-if="col._remove" class="fa-undo" alt="Undo Remove" />
<v-icon v-else class="fa-trash-o" alt="Remove" />
</button><!--
Add button
--><button type="button" :class="['x-add-btn', 'btn', 'btn-outline-secondary']"
v-if="i === columns.length - 1"
@click.prevent="addCol()">
<v-icon class="fa-plus" alt="Add Column" />
</button>
</td> </td>
</template> </tr>
</transition-group>
<td class="text-nowrap"><!-- </table>
Save button </div>
--><a href="" :class="['mr-1', 'btn', 'btn-outline-secondary', {'disabled': col._remove}]"
v-if="!newTable"
@click.prevent="toggleColEditing(col)">
<v-icon v-if="col._editing" class="fa-save" alt="Save" />
<v-icon v-else class="fa-pencil" alt="Edit" />
</a><!--
Delete button
--><button type="button" :class="delBtnClass(col)"
@click.prevent="toggleColDelete(col)">
<v-icon v-if="col._remove" class="fa-undo" alt="Undo Remove" />
<v-icon v-else class="fa-trash-o" alt="Remove" />
</button><!--
Add button
--><button type="button" :class="['x-add-btn', 'btn', 'btn-outline-secondary']"
v-if="i === columns.length - 1"
@click.prevent="addCol()">
<v-icon class="fa-plus" alt="Add Column" />
</button>
</td>
</tr>
</transition-group>
</table>
</div> </div>
</template> </template>
@ -342,13 +351,17 @@ export default {
let n = this.colPos(col) let n = this.colPos(col)
if (this.newTable) { if (this.newTable) {
if (this.columns.length == 1) return; // can't delete the last col if (this.columns.length == 1) return // can't delete the last col
// hard delete // hard delete
this.columns.splice(n, 1) this.columns.splice(n, 1)
} else { } else {
let remove = !col._remove let remove = !col._remove
if (col._new) {
if (!confirm(`Delete new column "${col.title}"? Any row data for this column will be lost.`)) return;
}
this.query({ this.query({
action: remove ? 'col.remove' : 'col.restore', action: remove ? 'col.remove' : 'col.restore',
id: col.id id: col.id
@ -398,6 +411,14 @@ export default {
return {width: `${w}rem`}; return {width: `${w}rem`};
}, },
resetOrder() {
this.query({
action: 'col.reset-sort'
}, (resp) => {
this.columns = resp.data;
})
}
} }
} }
</script> </script>

@ -44,7 +44,7 @@ Rows are identified by row._id, columns by col.id
</form> </form>
<button @click="saveAllChanges" type="button" <button @click="saveAllChanges" type="button"
:class="['btn', this.dirtyRows ? 'btn-info' : 'btn-outline-secondary']"> :class="['btn', this.dirtyRows ? 'btn-info' : ['btn-outline-secondary', 'disabled']]">
Save Rows Save Rows
</button> </button>
</div> </div>

@ -33,3 +33,15 @@
.noscript-hide { .noscript-hide {
display: none !important; display: none !important;
} }
.bold {
font-weight: bold;
}
.italic {
font-style: italic;
}
.no-decoration {
text-decoration: none !important;
}

@ -48,13 +48,7 @@ if (!isset($tab) || $tab == '') $tab = 'edit-rows';
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="text-success nav-link {{ $tab=='review' ? 'active' : '' }}" href="{{ $table->getDraftRoute('review') }}"> <a class="text-success nav-link {{ $tab=='review' ? 'active' : '' }}" href="{{ $table->getDraftRoute('review') }}">
@if(user()->ownsTable($table)) @icon(fa-save fa-pr)Review & Confirm
@icon(fa-save fa-pr){{--
--}}Review & Apply
@else
@icon(fa-paper-plane-o fa-pr){{--
--}}Review & Submit
@endif
</a> </a>
</li> </li>
<li class="nav-item pt-2 pr-2 opacity-fade ml-auto" style="opacity:0" id="draft-busy"> <li class="nav-item pt-2 pr-2 opacity-fade ml-auto" style="opacity:0" id="draft-busy">

@ -1,14 +1,111 @@
@php($tab='review') @php
$tab = 'review';
/** @var \App\Tables\Changeset $changeset */
/** @var \App\Models\Table $table */
$numChangedRows = count($changeset->rowUpdates);
$numNewRows = count($changeset->newRows);
$numRemovedRows = count($changeset->removedRows);
$anyRowChanges = $numChangedRows || $numNewRows || $numRemovedRows;
$numChangedColumns = count($changeset->columnUpdates);
$numNewColumns = count($changeset->newColumns);
$numRemovedColumns = count($changeset->removedColumns);
$colsReordered = !empty($changeset->columnOrder);
$anyColChanges = $numChangedColumns || $numNewColumns || $numRemovedColumns || $colsReordered;
$anyChanges = $anyRowChanges || $anyColChanges;
@endphp
@extends('table.propose.layout') @extends('table.propose.layout')
@section('tab-content') @section('tab-content')
@if(user()->ownsTable($table)) <div class="my-3 col-md-12">
<a href="" class="btn btn-outline-success" @tooltip(Save the changes and apply them as a new table revision)> <div class="mx-3">
@icon(fa-save fa-pr)Commit
</a> <?php Widget::setLayout(3,7) ?>
@else
<a href="" class="btn btn-outline-success" @tooltip(Submit your changes for review by the table owner)> {!! Widget::header(3, 'Change Summary') !!}
@icon(fa-save fa-pr)Submit
</a> <div class="row border-bottom border-top py-3">
@endif <div class="col-md-3 text-right">
Rows
</div>
<div class="col-md-7">
@if($anyRowChanges)
@if($numChangedRows)
<div class="text-info"><b>{{ $numChangedRows }}</b> changed</div>
@endif
@if($numNewRows)
<div class="text-success"><b>{{ $numNewRows }}</b> new</div>
@endif
@if($numRemovedRows)
<div class="text-danger"><b>{{ $numRemovedRows }}</b> removed</div>
@endif
@else
<span class="text-muted">No changes</span>
@endif
</div>
</div>
<div class="row border-bottom py-3 mb-3">
<div class="col-md-3 text-right">
Columns
</div>
<div class="col-md-7">
@if($anyColChanges)
@if($numChangedColumns)
<div class="text-info"><b>{{ $numChangedColumns }}</b> changed</div>
@endif
@if($numNewColumns)
<div class="text-success"><b>{{ $numNewColumns }}</b> new</div>
@endif
@if($numRemovedColumns)
<div class="text-danger"><b>{{ $numRemovedColumns }}</b> removed</div>
@endif
@if($colsReordered)
<div class="text-info">Order changed</div>
@endif
@else
<span class="text-muted">No changes</span>
@endif
</div>
</div>
{!! Widget::textarea('note', 'Summary')->value($changeset->note)
->help(user()->ownsTable($table) ?
"Describe changes you made to the table; this message will annotate the
new table revision." :
"Describe you suggested changes. The table owner will read this message
and review your changes before deciding whether to accept the proposal."
)->minHeight('8em')
!!}
<div class="row">
<div class="col-md-7 offset-md-3">
@if(user()->ownsTable($table))
<a href="" class="btn btn-outline-success {{$anyChanges?'':'disabled'}}">
@icon(fa-save fa-pr)Save & Apply
</a>
@else
<a href="" class="btn btn-outline-success {{$anyChanges?'':'disabled'}}">
@icon(fa-paper-plane-o fa-pr)Submit for review
</a>
@endif
</div>
</div>
</div>
</div>
@stop @stop

Loading…
Cancel
Save