Browse Source

Stat tree edit (wip)

Vijayakrishnan 3 years ago
parent
commit
952bbaab0c

+ 4 - 4
app/Http/Controllers/ClauseArgController.php

@@ -27,15 +27,15 @@ class ClauseArgController extends Controller
         return $this->pass();
     }
     public function update(Request $request) {
-        $clauseArg = ClauseArg::where('uid', $request->input('uid'))->first();
+        $clauseArg = ClauseArg::where('id', $request->input('id'))->first();
         if(!$clauseArg) return $this->fail('Clause arg not found!');
-        $clauseArg->arg_text = $request->input('arg_text');
-        $clauseArg->field_type = $request->input('field_type');
+        $clauseArg->arg_text = $request->input('argText');
+        $clauseArg->field_type = $request->input('fieldType');
         $clauseArg->save();
         return $this->pass();
     }
     public function remove(Request $request) {
-        $clauseArg = ClauseArg::where('uid', $request->input('uid'))->first();
+        $clauseArg = ClauseArg::where('id', $request->input('id'))->first();
         if(!$clauseArg) return $this->fail('Clause arg not found!');
         DB::select("delete from clause_arg where id = {$clauseArg->id}");
         return $this->pass();

+ 245 - 1
app/Http/Controllers/StatTreeController.php

@@ -73,8 +73,139 @@ class StatTreeController extends Controller
     }
 
     public function edit(Request $request, StatTree $statTree) {
+        return view('app.stat-tree.stat-trees.sub.edit', compact('statTree'));
+    }
+
+    public function clausesJSON(Request $request, StatTree $statTree) {
         $clauses = Clause::where('model', $statTree->model)->orderBy('position_index')->get();
-        return view('app.stat-tree.stat-trees.sub.edit', compact('statTree', 'clauses'));
+        $nodes = [];
+        foreach ($clauses as $clause) {
+
+            $children = [];
+
+            // clause text child
+            $children[] = [
+                "text" => $clause->clause_text,
+                "icon" => "fa fa-laptop-code text-primary text-sm",
+                "state" => [
+                    "opened" => false,
+                    "disabled" => false,
+                    "selected" => false,
+                ],
+                "li_attr" => [
+                    "type" => "clause_text"
+                ],
+                "system" => [
+                    "type" => "clause_text"
+                ]
+            ];
+
+            // clause arg children
+            foreach ($clause->clauseArgs as $clauseArg) {
+                $children[] = [
+                    "text" => $clauseArg->arg_text . '<span class="text-secondary text-sm ml-2">' . $clauseArg->field_type . '</span>',
+                    "icon" => "fa fa-cubes text-info",
+                    "state" => [
+                        "opened" => false,
+                        "disabled" => false,
+                        "selected" => false,
+                    ],
+                    "system" => [
+                        "type" => "clause_arg",
+                        "id" => $clauseArg->id,
+                        "uid" => $clauseArg->uid,
+                        "argText" => $clauseArg->arg_text,
+                        "fieldType" => $clauseArg->field_type,
+                        "clauseId" => $clause->id
+                    ]
+                ];
+            }
+
+            $nodes[] = [
+                "text" => $clause->label,
+                "state" => [
+                    "opened" => false,
+                    "disabled" => false,
+                    "selected" => false,
+                ],
+                "children" => $children,
+                "system" => [
+                    "type" => "clause",
+                    "id" => $clause->id,
+                    "uid" => $clause->uid,
+                    "model" => $clause->model,
+                    "question" => $clause->question,
+                    "answer" => $clause->answer,
+                    "label" => $clause->label,
+                    "clauseText" => $clause->clause_text,
+                    "clauseId" => $clause->id
+                ]
+            ];
+        }
+
+        return json_encode($nodes);
+    }
+
+    public function linesJSON(Request $request, StatTree $statTree) {
+        $nodes = [];
+        foreach ($statTree->rootLines as $rootLine) {
+            $nodes[] = $this->lineObject($rootLine);
+        }
+        return json_encode($nodes);
+    }
+
+    private function lineObject(StatTreeLine  $line) {
+        $columns = [];
+        foreach ($line->reportColumns as $column) {
+            $columns[] = [
+                "label" => $column->label,
+                "display_key" => $column->display_key,
+            ];
+        }
+
+        $clauses = [];
+        foreach($line->lineClauses as $lineClause) {
+            $args = [];
+            foreach($lineClause->statTreeLineClauseArgs as $stlcArg) {
+                if($stlcArg->clauseArg) {
+                    $args[] = [
+                        "arg_text" => $stlcArg->clauseArg->arg_text,
+                        "field_type" => $stlcArg->clauseArg->field_type,
+                        "default_value" => $stlcArg->default_value,
+                        "access_level" => $stlcArg->access_level,
+                    ];
+                }
+            }
+            $clauses[] = [
+                "clause_id" => $lineClause->clause_id,
+                "clause_label" => $lineClause->clause_label,
+                "position_index" => $lineClause->position_index,
+                "args" => $args
+            ];
+        }
+        $children = [];
+        foreach ($line->children as $child) {
+            $children[] = $this->lineObject($child);
+        }
+        return [
+            "text" => $line->displayLabel() . '<span class="ml-2 text-secondary">(' . (is_null($line->last_refresh_count) ? '…' : $line->last_refresh_count) . ')</span>',
+            "state" => [
+                "opened" => true,
+                "disabled" => false,
+                "selected" => false,
+            ],
+            "children" => $children,
+            "system" => [
+                "type" => "stat_tree_line",
+                "id" => $line->id,
+                "uid" => $line->uid,
+                "displayLabel" => $line->displayLabel(),
+                "lastRefreshCount" => $line->last_refresh_count,
+                "treeOrderPositionIndex" => $line->tree_order_position_index,
+                "columns" => $columns,
+                "clauses" => $clauses
+            ]
+        ];
     }
 
     public function delete(Request $request){
@@ -217,4 +348,117 @@ class StatTreeController extends Controller
         DB::commit();
         return $this->pass();
     }
+
+
+    public function replaceAllLinesJSON(Request $request) {
+
+        $statTree = StatTree::where('uid', $request->get('uid'))->first();
+        if(!$statTree) return $this->fail("Stat tree does not exist!");
+
+        $lines = json_decode($request->get('data'));
+
+        DB::beginTransaction();
+
+        // cleanup junk
+        DB::statement("DELETE FROM stat_tree_line WHERE stat_tree_id = :stat_tree_id", ['stat_tree_id' => $statTree->id]);
+        DB::statement("DELETE FROM stat_tree_line_clause WHERE stat_tree_id = :stat_tree_id", ['stat_tree_id' => $statTree->id]);
+        DB::statement("DELETE FROM stat_tree_line_clause_arg WHERE stat_tree_id = :stat_tree_id", ['stat_tree_id' => $statTree->id]);
+        DB::statement("DELETE FROM stat_tree_line_clause_arg_value WHERE stat_tree_id = :stat_tree_id", ['stat_tree_id' => $statTree->id]);
+        DB::statement("DELETE FROM stat_tree_line_report_column WHERE stat_tree_id = :stat_tree_id", ['stat_tree_id' => $statTree->id]);
+
+        // process
+        for ($i=0; $i<count($lines); $i++) {
+            $result = $this->saveStatTreeLine($lines[$i], $i, null, $statTree);
+            if($result !== TRUE) {
+                DB::rollBack();
+                return $result;
+            }
+        }
+
+        DB::commit();
+        return $this->pass();
+    }
+
+    private function saveStatTreeLine($line, $position, $parentLine, $statTree) {
+
+        // saved tree line
+        $nextId = DB::select("select nextval('stat_tree_line_id_seq')");
+        $statTreeLine = new StatTreeLine;
+        $statTreeLine->id = $nextId[0]->nextval;
+        $statTreeLine->uid = Uuid::uuid4();
+        $statTreeLine->stat_tree_id = $statTree->id;
+        $statTreeLine->tree_order_position_index = $position;
+        $statTreeLine->last_refresh_count = null;
+        $statTreeLine->tsv_text_for_report_columns = null;
+        $statTreeLine->parent_stat_tree_line_id = $parentLine ? $parentLine->id : null;
+        $statTreeLine->save();
+
+        // clauses
+        for ($i=0; $i<count($line->clauses); $i++) {
+            $clause = Clause::where('label', $line->clauses[$i]->clause_label)->where('model', 'ilike', $statTree->model)->first();
+            if(!$clause) {
+                return $this->fail('No clause record found for ' . $line->clauses[$i]->clause_label);
+            }
+            $nextId = DB::select("select nextval('stat_tree_line_clause_id_seq')");
+            $statTreeLineClause = new StatTreeLineClause;
+            $statTreeLineClause->id = $nextId[0]->nextval;
+            $statTreeLineClause->uid = Uuid::uuid4();
+            $statTreeLineClause->stat_tree_line_id = $statTreeLine->id;
+            $statTreeLineClause->clause_id = $clause->id;
+            $statTreeLineClause->clause_label = $line->clauses[$i]->clause_label;
+            $statTreeLineClause->position_index = $i;
+            $statTreeLineClause->stat_tree_id = $statTree->id;
+            $statTreeLineClause->save();
+
+            // args
+            foreach ($clause->clauseArgs as $clauseArg) {
+                $statTreeLineClauseArg = new StatTreeLineClauseArg();
+                $nextId = DB::select("select nextval('stat_tree_line_clause_arg_id_seq')");
+                $statTreeLineClauseArg->id = $nextId[0]->nextval;
+                $statTreeLineClauseArg->stat_tree_line_clause_id = $statTreeLineClause->id;
+                $statTreeLineClauseArg->clause_arg_id = $clauseArg->id;
+                $statTreeLineClauseArg->default_value = null;
+                $statTreeLineClauseArg->access_level = 'ADMIN';
+
+                // find the arg matching text and type from line->args and use that
+                for ($j=0; $j<count($line->clauses[$i]->args); $j++) {
+                    if($line->clauses[$i]->args[$j]->arg_text === $clauseArg->arg_text &&
+                        $line->clauses[$i]->args[$j]->field_type === $clauseArg->field_type) {
+                        $statTreeLineClauseArg->default_value = $line->clauses[$i]->args[$j]->default_value;
+                        $statTreeLineClauseArg->access_level = $line->clauses[$i]->args[$j]->access_level;
+                        break;
+                    }
+                }
+
+                $statTreeLineClauseArg->stat_tree_id = $statTree->id;
+                $statTreeLineClauseArg->save();
+            }
+
+        }
+
+        // columns
+        for ($i=0; $i<count($line->columns); $i++) {
+            $column = new StatTreeLineReportColumn();
+            $nextId = DB::select("select nextval('stat_tree_line_report_column_id_seq')");
+            $column->id = $nextId[0]->nextval;
+            $column->uid = Uuid::uuid4();
+            $column->stat_tree_line_id = $statTreeLine->id;
+            $column->label = $line->columns[$i]->label;
+            $column->display_key = $line->columns[$i]->display_key;
+            $column->position_index = $i;
+            $column->stat_tree_id = $statTree->id;
+            $column->save();
+        }
+
+        // child lines
+        for ($i=0; $i<count($line->children); $i++) {
+            $result = $this->saveStatTreeLine($line->children[$i], $i, $statTreeLine, $statTree);
+            if($result !== TRUE) {
+                DB::rollBack();
+                return $result;
+            }
+        }
+
+        return TRUE;
+    }
 }

+ 1 - 1
app/Models/StatTree.php

@@ -21,6 +21,6 @@ class StatTree extends Model
     }
 
     public function rootLines(){
-        return $this->hasMany(StatTreeLine::class, 'stat_tree_id', 'id')->whereNull('parent_stat_tree_line_id')->orderBy('id', 'ASC');
+        return $this->hasMany(StatTreeLine::class, 'stat_tree_id', 'id')->whereNull('parent_stat_tree_line_id')->orderBy('tree_order_position_index', 'ASC');
     }
 }

+ 2 - 1
app/Models/StatTreeLine.php

@@ -16,7 +16,8 @@ class StatTreeLine extends Model
     }
 
     public function lineClauses(){
-        return $this->hasMany(StatTreeLineClause::class, 'stat_tree_line_id', 'id');
+        return $this->hasMany(StatTreeLineClause::class, 'stat_tree_line_id', 'id')
+            ->orderBy('position_index');
     }
     public function statTree(){
         return $this->hasOne(StatTree::class, 'id', 'stat_tree_id');

+ 610 - 286
resources/views/app/stat-tree/stat-trees/sub/edit.blade.php

@@ -4,15 +4,269 @@
 
     <div id="statTreeEdit">
         <div id="statTreeView" class="row">
+
+            <!-- clauses -->
             <div class="col-3 pr-0">
-                @include('app.stat-tree.clauses-edit')
+                <div class="d-flex align-items-baseline mb-2">
+                    <h6 class="font-weight-bold m-0">Available Clauses</h6>
+                    <div moe relative wide class="ml-3">
+                        <a href="#" start show>+ Add</a>
+                        <form url="{{ route("practice-management.api.clause.create") }}" class="frm-clause-add-edit" hook="reloadClausesTree">
+                            @csrf
+                            <div class="mb-2">
+                                <label class="text-secondary text-sm mb-1">Model</label>
+                                <input type="text" class="form-control form-control-sm" name="model" readonly value="{{$statTree->model}}">
+                            </div>
+                            <div class="mb-2">
+                                <label class="text-secondary text-sm mb-1">Question</label>
+                                <input type="text" class="form-control form-control-sm" name="question" autofocus>
+                            </div>
+                            <div class="mb-2">
+                                <label class="text-secondary text-sm mb-1">Answer</label>
+                                <input type="text" class="form-control form-control-sm" name="answer">
+                            </div>
+                            <div class="mb-2">
+                                <label class="text-secondary text-sm mb-1">Label</label>
+                                <input type="text" class="form-control form-control-sm" name="label" readonly>
+                            </div>
+                            <div class="mb-2">
+                                <label class="text-secondary text-sm mb-1">Clause Text</label>
+                                <input type="text" class="form-control form-control-sm" name="clauseText">
+                            </div>
+                            <div class="d-flex align-items-center">
+                                <button class="btn btn-sm btn-primary mr-2" type="button" submit>Save</button>
+                                <button class="btn btn-sm btn-default mr-2 border" type="button" cancel>Cancel</button>
+                            </div>
+                        </form>
+                    </div>
+                    <a href="#" class="clause-expand-all ml-3" title="Expand All"><i class="fa fa-angle-double-down text-secondary"></i></a>
+                    <a href="#" class="clause-collapse-all ml-2" title="Collapse All"><i class="fa fa-angle-double-up text-secondary"></i></a>
+                </div>
+                <div class="mb-3 overflow-auto clauses-view pb-5" id="clauses-view-{{$statTree->id}}"></div>
+
+                <!-- hidden moes invoked due to context actions -->
+                <div class="border mb-3 p-2 position-absolute" style="left: -10000px; top: -10000px;">
+                    <div class="d-inline-flex align-items-baseline">
+
+                        <!-- edit clause -->
+                        <div moe center relative wide class="ml-3 mr-2" id="edit-clause-moe">
+                            <a href="#" start show><i class="text-sm fa fa-edit on-hover-opaque"></i></a>
+                            <form url="{{ route("practice-management.api.clause.update") }}" class="frm-clause-add-edit" center hook="reloadClausesTree">
+                                @csrf
+                                <input type="hidden" name="uid">
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Model</label>
+                                    <input type="text" class="form-control form-control-sm" name="model" readonly>
+                                </div>
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Question</label>
+                                    <input type="text" class="form-control form-control-sm" name="question">
+                                </div>
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Answer</label>
+                                    <input type="text" class="form-control form-control-sm" name="answer">
+                                </div>
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Label</label>
+                                    <input type="text" class="form-control form-control-sm" name="label" readonly>
+                                </div>
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Clause Text</label>
+                                    <input type="text" class="form-control form-control-sm" name="clauseText">
+                                </div>
+                                <div class="d-flex align-items-center">
+                                    <button class="btn btn-sm btn-primary mr-2" type="button" submit>Save</button>
+                                    <button class="btn btn-sm btn-default mr-2 border" type="button" cancel>Cancel</button>
+                                </div>
+                            </form>
+                        </div>
+
+                        <!-- remove clause -->
+                        <div moe relative center id="remove-clause-moe">
+                            <a href="#" start show><i class="text-sm fa fa-trash-alt on-hover-opaque text-danger"></i></a>
+                            <form url="{{ route("practice-management.api.clause.remove") }}" center hook="reloadClausesTree">
+                                @csrf
+                                <input type="hidden" name="uid">
+                                <p>Are you sure?</p>
+                                <div class="d-flex align-items-center">
+                                    <button class="btn btn-sm btn-danger mr-2" type="button" submit>Remove</button>
+                                    <button class="btn btn-sm btn-default mr-2 border" type="button" cancel>Cancel</button>
+                                </div>
+                            </form>
+                        </div>
+
+                        <!-- add clause arg -->
+                        <div moe relative center id="add-clause-arg-moe">
+                            <a href="#" start show><i class="text-sm fa fa-trash-alt on-hover-opaque text-danger"></i></a>
+                            <form url="{{ route("practice-management.api.clauseArg.create") }}" center hook="reloadClausesTree">
+                                @csrf
+                                <input type="hidden" name="clauseId">
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Arg Text</label>
+                                    <input type="text" class="form-control form-control-sm" name="argText" required>
+                                </div>
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Field Type</label>
+                                    <select class="form-control form-control-sm" name="fieldType" required>
+                                        <option value="">-- select --</option>
+                                        <option value="number">Number</option>
+                                        <option value="date">Date</option>
+                                        <option value="string">String</option>
+                                        <option value="bool">Boolean</option>
+                                    </select>
+                                </div>
+                                <div class="d-flex align-items-center">
+                                    <button class="btn btn-sm btn-primary mr-2" type="button" submit>Save</button>
+                                    <button class="btn btn-sm btn-default mr-2 border" type="button" cancel>Cancel</button>
+                                </div>
+                            </form>
+                        </div>
+
+                        <!-- edit clause arg -->
+                        <div moe relative center id="edit-clause-arg-moe">
+                            <a href="#" start show><i class="text-sm fa fa-trash-alt on-hover-opaque text-danger"></i></a>
+                            <form url="{{ route("practice-management.api.clauseArg.update") }}" center hook="reloadClausesTree">
+                                @csrf
+                                <input type="hidden" name="id">
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Arg Text</label>
+                                    <input type="text" class="form-control form-control-sm" name="argText" required>
+                                </div>
+                                <div class="mb-2">
+                                    <label class="text-secondary text-sm mb-1">Field Type</label>
+                                    <select class="form-control form-control-sm" name="fieldType" required>
+                                        <option value="">-- select --</option>
+                                        <option value="number">Number</option>
+                                        <option value="date">Date</option>
+                                        <option value="string">String</option>
+                                        <option value="bool">Boolean</option>
+                                    </select>
+                                </div>
+                                <div class="d-flex align-items-center">
+                                    <button class="btn btn-sm btn-primary mr-2" type="button" submit>Save</button>
+                                    <button class="btn btn-sm btn-default mr-2 border" type="button" cancel>Cancel</button>
+                                </div>
+                            </form>
+                        </div>
+
+                        <!-- remove clause arg -->
+                        <div moe relative center id="remove-clause-arg-moe">
+                            <a href="#" start show><i class="text-sm fa fa-trash-alt on-hover-opaque text-danger"></i></a>
+                            <form url="{{ route("practice-management.api.clauseArg.remove") }}" center hook="reloadClausesTree">
+                                @csrf
+                                <input type="hidden" name="id">
+                                <p>Are you sure?</p>
+                                <div class="d-flex align-items-center">
+                                    <button class="btn btn-sm btn-danger mr-2" type="button" submit>Remove</button>
+                                    <button class="btn btn-sm btn-default mr-2 border" type="button" cancel>Cancel</button>
+                                </div>
+                            </form>
+                        </div>
+
+
+                    </div>
+                </div>
+
             </div>
+
+
+            <!-- tree -->
             <div class="col-5 pr-0 pl-3 border-left tree-column">
-                @include('app.stat-tree.tree-edit-v2', ['slug' => $statTree->slug])
+                <div class="d-flex align-items-baseline mb-2">
+                    <h6 class="font-weight-bold m-0">{{$statTree->name}}</h6>
+                    <a href="#" id="refresh-counts" class="ml-3">Refresh Counts</a>
+                    <a href="#" class="tree-expand-all ml-3" title="Expand All"><i class="fa fa-angle-double-down text-secondary"></i></a>
+                    <a href="#" class="tree-collapse-all ml-2" title="Collapse All"><i class="fa fa-angle-double-up text-secondary"></i></a>
+                    <div class="d-none if-changed ml-auto mr-2">
+                        <a href="#" class="btn btn-sm btn-primary text-white" id="btn-save-tree">Save</a>
+                        <a href="#" class="ml-2 btn btn-sm btn-default border text-dark" onclick="return fastReload()">Reset</a>
+                    </div>
+                </div>
+                <div class="stat-tree-view mb-3 overflow-auto mr-2 min-height-300px" id="stat-tree-view-{{$statTree->id}}"></div>
+                @if(!$statTree->rootLines || !count($statTree->rootLines))
+                    <p class="text-sm text-secondary font-italic">Drag clauses from the left and drop it above to begin.</p>
+                @endif
+
+                <!-- hidden moes invoked due to context actions -->
+                <div class="border mb-3 p-2 position-absolute" style="left: -10000px; top: -10000px;">
+                    <div class="d-inline-flex align-items-baseline">
+
+                        <!-- remove line -->
+                        <div moe relative center id="remove-line-moe">
+                            <a href="#" start show><i class="text-sm fa fa-trash-alt on-hover-opaque text-danger"></i></a>
+                            <form url="{{ route("practice-management.api.statTreeLine.remove") }}" center hook="reloadStatTree">
+                                @csrf
+                                <input type="hidden" name="uid">
+                                <p>Are you sure?</p>
+                                <div class="d-flex align-items-center">
+                                    <button class="btn btn-sm btn-danger mr-2" type="button" submit>Remove</button>
+                                    <button class="btn btn-sm btn-default mr-2 border" type="button" cancel>Cancel</button>
+                                </div>
+                            </form>
+                        </div>
+
+                    </div>
+                </div>
+
             </div>
+
+
+            <!-- selected line properties -->
             <div class="col-4 pl-3 border-left setup-column">
-                @include('app.stat-tree.stat-tree-lines.line-properties')
+                <div id="line-properties-column" class="d-none">
+                    <div class="mb-3 d-flex align-items-baseline pb-2 border-bottom font-weight-bold">
+                        <span line-label></span>
+                    </div>
+                    <div class="d-flex align-items-baseline mb-2">
+                        <h6 class="font-weight-bold m-0 text-secondary">Argument Values</h6>
+                        <a href="#" class="ml-3 text-sm" onclick="$('.parent-arg').toggle(); return false;">Toggle Parent Clause Args</a>
+                    </div>
+                    <div>
+                        <table class="table table-sm table-bordered table-striped">
+                            <thead>
+                            <tr>
+                                <th class="border-bottom-0 w-35">Arg</th>
+                                <th class="border-bottom-0 w-35">Value</th>
+                                <th class="border-bottom-0">Access</th>
+                            </tr>
+                            </thead>
+                            <tbody line-args>
+                            </tbody>
+                        </table>
+                    </div>
+                    <div class="d-flex align-items-baseline mb-2 border-top pt-3">
+                        <h6 class="font-weight-bold m-0 text-secondary">Report Columns</h6>
+                    </div>
+                    <div>
+                        <div class="mb-2 d-flex align-items-baseline">
+                            <span class="text-secondary mr-1 text-nowrap">Quick Add:</span>
+                            <div class="flex-grow-1 position-relative">
+                                <input type="text"
+                                       name="displayKey"
+                                       class="form-control form-control-sm"
+                                       placeholder="Column name"
+                                       stag-suggest stag-suggest-left
+                                       stag-suggest-ep="/column-suggest"
+                                       stag-suggest-extra="table={{$statTree->model}}"
+                                       autocomplete="off">
+                            </div>
+                        </div>
+                        <table class="table table-sm table-bordered table-striped">
+                            <thead>
+                            <tr>
+                                <th class="border-bottom-0 width-30px">#</th>
+                                <th class="border-bottom-0 w-35">Label</th>
+                                <th class="border-bottom-0">Column</th>
+                                <th class="border-bottom-0 width-60px"></th>
+                            </tr>
+                            </thead>
+                            <tbody line-columns>
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
             </div>
+
         </div>
 
     </div>
@@ -20,347 +274,384 @@
     <script>
         (function() {
             function init() {
-                $(document)
-                    .off('input change paste', '.frm-clause-add-edit input[name="question"], .frm-clause-add-edit input[name="answer"]')
-                    .on('input change paste', '.frm-clause-add-edit input[name="question"], .frm-clause-add-edit input[name="answer"]', function() {
-                        let form = $(this).closest('.frm-clause-add-edit');
-                        let label = $.trim(form.find('input[name="question"]').val()) + ' ' +
-                            $.trim(form.find('input[name="answer"]').val());
-                        form.find('input[name="label"]').val(label);
-                    });
 
-                let treePayload = '';
+                let linePropsColumn = $('#line-properties-column');
 
-                function selectedNode() {
-                    let selected = $('#stat-tree-view-{{$statTree->id}}>ul').jstree('get_selected', false);
-                    if(selected && selected.length) {
-                        selected = $('#' + selected[0]);
-                    }
-                    if(!selected || !selected.length) return;
-                    return selected.find('>a.jstree-anchor>.st-node').first();
-                }
-
-                function getSelectedNodeArgs() {
-                    let selected = selectedNode();
-                    let argsField = selected.find('>[data-name="args"]').first();
-                    let args = argsField.val();
-                    if(!!args) return JSON.parse(args);
-                    return [];
-                }
-
-                function getSelectedNodeColumns() {
-                    let selected = selectedNode();
-                    let columnsField = selected.find('>[data-name="columns"]').first();
-                    let columns = columnsField.val();
-                    if(!!columns) return JSON.parse(columns);
-                    return [];
-                }
-
-                function setSelectedNodeColumns(columns) {
-                    let selected = selectedNode();
-                    let columnsField = selected.find('>[data-name="columns"]').first();
-                    columnsField.val(JSON.stringify(columns));
-                }
-
-                function getNodes(_ul, _indent, _lines) {
-                    _ul.find('>li').each(function() {
-                        let node = $(this).find('>a.jstree-anchor>.st-node');
-                        let line = _indent + node.find('>span').first().text();
-                        let columnsElem = node.find('>[data-name="columns"]').first(), columns = [];
-                        if(columnsElem && columnsElem.length && !!columnsElem.val()) {
-                            let columnsParsed = JSON.parse(columnsElem.val());
-                            for (let i = 0; i < columnsParsed.length; i++) {
-                                columns.push(columnsParsed[i].display_key+'|'+columnsParsed[i].label);
-                            }
-                            columns = columns.join(',');
+                function fillAndInvokeMoe(_id, _node) {
+                    let moe = $(_id);
+                    moe.find('input[name], select[name]').each(function() {
+                        let name = $(this).attr('name');
+                        if(!!_node.original.system[name]) {
+                            $(this).val(!!_node.original.system[name] ? _node.original.system[name] : '');
                         }
-                        line = line + '==>' + columns;
-                        _lines.push(line);
-                        $(this).find('>ul.jstree-children').each(function() {
-                            getNodes($(this), _indent + "\t", _lines);
-                        });
-                    })
+                    });
+                    moe.find('a[start]').trigger('click');
                 }
 
-                function generateTextRepresentation(_e, _data) {
+                // clauses tree
+                let ClausesTree = {
+                    el: $('#clauses-view-{{$statTree->id}}'),
+                    load: function() {
 
-                    $('#stat-tree-view-{{$statTree->id}}').jstree('open_all');
-                    $('#stat-tree-view-{{$statTree->id}} [data-type="meta"]').remove();
-
-                    let nodes = [];
-                    getNodes($('#stat-tree-view-{{$statTree->id}}>ul').first(), '', nodes);
-
-                    let converted = tsvToArray.getAutoFilledTabContent(nodes.join("\n"));
-
-                    let maxColumn = 0, columns = [];
-                    try {
-                        let rows = converted.split("\n");
-                        rows.map((row, index) => {
-                            columns[index] = row.split("\t");
-                            const length = columns[index].length;
-                            if (length > maxColumn) {
-                                maxColumn = length
-                            }
-                            const emptyFields = maxColumn - length;
-                            for (ii = 0; ii < emptyFields; ii++) {
-                                columns[index].push('');
-                            }
-                        });
-                    } catch (error) {
-                        console.error(error);
-                        this.rows = [];
+                        // destroy if existing
+                        try {
+                            this.el.jstree('destroy');
+                        }
+                        catch (e) {}
+
+                        // get data
+                        $.get('{{ route('practice-management.statTrees.view.clausesJSON', $statTree) }}', _data => {
+
+                            // init tree with data
+                            this.el
+                                .jstree({
+                                    "core": {
+                                        "multiple": false,
+                                        "animation": 0,
+                                        'data': _data
+                                    },
+                                    "dnd": {
+                                        "is_draggable": function(_node, _e) {
+                                            return _node[0].original.system.type === 'clause';
+                                        }
+                                    },
+                                    "contextmenu": {
+                                        show_at_node: false,
+                                        items: function (node) {
+                                            console.log('ALIX',node.original.system)
+                                            if(node.original.system.type === 'clause') { // clause
+                                                return {
+                                                    "edit": {
+                                                        "label": "<span class='text-sm'>Edit Clause</span>",
+                                                        "action": function (obj) {
+                                                            fillAndInvokeMoe('#edit-clause-moe', node);
+                                                        }
+                                                    },
+                                                    "remove": {
+                                                        "label": "<span class='text-sm'>Remove Clause</span>",
+                                                        "action": function (obj) {
+                                                            fillAndInvokeMoe('#remove-clause-moe', node);
+                                                        }
+                                                    },
+                                                    "add_arg": {
+                                                        "label": "<span class='text-sm'>Add Clause Arg</span>",
+                                                        "action": function (obj) {
+                                                            fillAndInvokeMoe('#add-clause-arg-moe', node);
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                            else if(node.original.system.type === 'clause_arg') { // clause
+                                                return {
+                                                    "edit": {
+                                                        "label": "<span class='text-sm'>Edit Arg</span>",
+                                                        "action": function (obj) {
+                                                            fillAndInvokeMoe('#edit-clause-arg-moe', node);
+                                                        }
+                                                    },
+                                                    "remove": {
+                                                        "label": "<span class='text-sm'>Remove Arg</span>",
+                                                        "action": function (obj) {
+                                                            fillAndInvokeMoe('#remove-clause-arg-moe', node);
+                                                        }
+                                                    }
+                                                };
+                                            }
+                                        },
+                                    },
+                                    "plugins": [
+                                        "wholerow",
+                                        "dnd",
+                                        "contextmenu",
+                                        "state"
+                                    ]
+                                });
+                        }, 'json');
                     }
+                };
 
-                    treePayload = JSON.stringify(columns);
-
-                }
-
-                function autoSaveChanges() {
-                    setTimeout(() => {
-                        saveStatTree();
-                    }, 0);
-                }
+                // stat tree
+                let StatTree = {
+                    el: $('#stat-tree-view-{{$statTree->id}}'),
+                    load: function() {
 
-                function saveStatTree() {
-                    generateTextRepresentation();
-                    $.post('{{ route("practice-management.api.statTree.replaceAllLines") }}', {
-                        statTreeID: "{{ $statTree->id }}",
-                        data: treePayload
-                    }, function (response) {
-                        if(!hasResponseError(response)) {
-                            //fastReload();
-                            // need to fix this!
-                            showMask();
-                            window.top.location.reload();
+                        // destroy if existing
+                        try {
+                            this.el.jstree('destroy');
                         }
-                    }, 'json');
-                }
+                        catch (e) {}
+
+                        // get data
+                        $.get('{{ route('practice-management.statTrees.view.linesJSON', $statTree) }}', _data => {
+
+                            // init tree with data
+                            this.el
+                                .on('move_node.jstree', function(node, original) { StatTree.dropped(node, original); })
+                                .on('copy_node.jstree', function() { StatTree.dropped.apply(StatTree, arguments); })
+                                // .on('move_node.jstree', function() { StatTree.save(); })
+                                // .on('copy_node.jstree', function() { StatTree.save(); })
+                                .on('select_node.jstree', () => { StatTree.onSelected(); })
+                                .on('deselect_node.jstree', () => { StatTree.onDeselected(); })
+                                .jstree({
+                                    "core": {
+                                        "check_callback": true,
+                                        "multiple": false,
+                                        "animation": 0,
+                                        'data': _data
+                                    },
+                                    "dnd": {
+                                        "is_draggable": function(_node, _e) {
+                                            return _node[0].original.system && _node[0].original.system.type === 'stat_tree_line';
+                                        }
+                                    },
+                                    "contextmenu": {
+                                        show_at_node: false,
+                                        items: function (node) {
+                                            console.log('ALIX',node);
+                                            if(node.original.system.type === 'stat_tree_line') { // stat_tree_line
+                                                return {
+
+                                                    "data": {
+                                                        "label": "<span class='text-sm'>View Data</span>",
+                                                        "action": function (obj) {
+                                                            openDynamicStagPopup('/practice-management/stat-tree-lines/view-data/' + node.original.system.uid,
+                                                                null,
+                                                                node.original.system.displayLabel,
+                                                                false,
+                                                                'medium');
+                                                        },
+                                                    },
+                                                    "remove": {
+                                                        "label": "<span class='text-sm'>Remove Node</span>",
+                                                        "action": function (obj) {
+                                                            fillAndInvokeMoe('#remove-line-moe', node);
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                        },
+                                    },
+                                    "plugins": [
+                                        "wholerow",
+                                        "dnd",
+                                        "contextmenu",
+                                        "state"
+                                    ]
+                                });
+                        }, 'json');
+                    },
 
-                function onDeselected(_e, _data) {
-                    let rptColumn = $('#line-properties-column').addClass('d-none');
-                }
+                    dropped: function(_e, _data) {
+                        if(_data.original && _data.original.original && _data.original.original.system &&
+                            _data.original.original.system.type === 'clause') {
+                            _data.node.original.system = _data.original.original.system;
+                        }
+                        StatTree.save();
+                    },
+
+                    save: function() {
+                        setTimeout(() => {
+                            StatTree.payload();
+                            $.post('{{ route("practice-management.api.statTree.replaceAllLinesJSON") }}', {
+                                uid: "{{ $statTree->uid }}",
+                                data: StatTree.payload()
+                            }, function (response) {
+                                if(!hasResponseError(response)) {
+                                    StatTree.load();
+                                    // TODO: saved indicator
+                                }
+                            }, 'json');
+                            StatTree.onSelected();
+                        }, 0);
+                    },
+
+                    payload: function() {
+                        let raw = this.el.jstree('get_json');
+                        let nodes = [];
+                        for (let i = 0; i < raw.length; i++) {
+                            nodes.push(this.nodePayload(raw[i].id));
+                        }
+                        return JSON.stringify(nodes);
+                    },
+
+                    nodePayload: function(_id, _parentClauses = null) {
+                        let node = this.el.jstree('get_node', _id);
+                        let payload = {};
+                        let clauses = _parentClauses ? JSON.parse(JSON.stringify(_parentClauses)) : [];
+                        if(node.original.system.type === 'clause') {
+                            clauses.push({
+                                clause_label: node.original.system.label,
+                                args: []
+                            });
+                            payload = {
+                                displayLabel: node.original.system.label,
+                                clauses: clauses,
+                                columns: [],
+                                children: [],
+                            };
+                        }
+                        else if(node.original.system.type === 'stat_tree_line') {
+                            clauses.push(node.original.system.clauses[node.original.system.clauses.length - 1]);
+                            payload = {
+                                displayLabel: node.original.system.displayLabel,
+                                clauses: clauses,
+                                columns: node.original.system.columns,
+                            };
+                            let children = [];
+                            for (let i = 0; i < node.children.length; i++) {
+                                children.push(this.nodePayload(node.children[i], clauses));
+                            }
+                            payload.children = children;
+                        }
+                        return payload;
+                    },
 
-                function onSelected(_e, _data) {
-                    let selected = selectedNode();
-
-                    let rptColumn = $('#line-properties-column').removeClass('d-none');
-                    rptColumn.find('[line-label]').text(selected.find('>span').first().text());
-
-                    // fill args
-                    let tbody = rptColumn.find('[line-args]');
-                    let args = getSelectedNodeArgs();
-                    tbody.empty();
-                    if(args && args.length) {
-                        for (let i = 0; i < args.length; i++) {
-                            $('<tr/>')
-                                .append($('<td/>').html(args[i].arg_text + ' <span class="text-secondary text-sm">(' + args[i].field_type + ')</span>'))
-                                .append($('<td/>').text(args[i].default_value).append('<a href="#" class="edit-arg-value ml-2" data-uid="' + args[i].uid + '"><i class="fa fa-edit text-primary text-sm on-hover-opaque"></i></a>'))
-                                .append($('<td/>').text(args[i].access_level).append('<a href="#" class="edit-arg-access-level ml-2" data-uid="' + args[i].uid + '"><i class="fa fa-edit text-primary text-sm on-hover-opaque"></i></a>'))
-                                .appendTo(tbody);
+                    selectedNode: function() {
+                        let selected = this.el.jstree('get_selected', true);
+                        if(selected && selected.length) {
+                            return selected[0];
                         }
-                    }
+                        return false;
+                    },
 
-                    tbody = rptColumn.find('[line-columns]');
-
-                    let columns = getSelectedNodeColumns();
-
-                    tbody.empty();
-
-                    if(columns && columns.length) {
-                        for (let i = 0; i < columns.length; i++) {
-                            $('<tr/>')
-                                .append($('<td/>').text(i + 1))
-                                .append($('<td/>').text(columns[i].label))
-                                .append($('<td/>').text(columns[i].display_key))
-                                .append(
-                                    $('<td/>')
-                                        .addClass('text-right')
-                                        .append(i > 0 ? '<a href="#" class="move-column-up mr-2" data-index="' + i + '"><i class="fa fa-arrow-up text-primary on-hover-opaque"></i></a>': '')
-                                        .append(i < columns.length - 1 ? '<a href="#" class="move-column-down mr-2" data-index="' + i + '"><i class="fa fa-arrow-down text-primary on-hover-opaque"></i></a>': '')
-                                        .append('<a href="#" class="remove-column" data-index="' + i + '"><i class="fa fa-trash-alt text-danger on-hover-opaque"></i></a>')
-                                )
-                                .appendTo(tbody);
+                    getSelectedNodeArgs: function() {
+                        let selected = this.selectedNode();
+                        if(selected) {
+                            return selected.original.system.args;
                         }
-                    }
+                        return [];
+                    },
 
-                }
+                    getSelectedNodeColumns: function() {
+                        let selected = this.selectedNode();
+                        if(selected) {
+                            return selected.original.system.columns;
+                        }
+                        return [];
+                    },
 
-                $('#stat-tree-view-{{$statTree->id}}')
-                    //.on('changed.jstree', autoSaveChanges)
-                    .on('move_node.jstree', autoSaveChanges)
-                    .on('copy_node.jstree', autoSaveChanges)
-                    .on('select_node.jstree', onSelected)
-                    .on('deselect_node.jstree', onDeselected)
-                    .jstree({
-                        "core": {
-                            "check_callback": true,
-                            "multiple": false,
-                            "animation": 0
-                        },
-                        "contextmenu": {
-                            show_at_node: false,
-                            items: function ($node) {
-                                return {
-                                    "data": {
-                                        "label": "<span class='text-sm'>View Data</span>",
-                                        "action": function (obj) {
-                                            let uid = $(obj.reference).find('>div.stat-tree-line').attr('data-line-uid');
-                                            openDynamicStagPopup('/practice-management/stat-tree-lines/view-data/' + uid,
-                                                null,
-                                                $(obj.reference).find('>div.stat-tree-line>span').first().text(),
-                                                false,
-                                                'medium');
-                                        },
-                                    },
-                                    "remove": {
-                                        "label": "<span class='text-sm'>Remove Node</span>",
-                                        "action": function (obj) {
-                                            let id = $(obj.reference).find('>div.stat-tree-line').attr('data-line-id');
-                                            $('.remove-line-moe-' + id).find('a[start]').trigger('click');
-                                        }
+                    setSelectedNodeColumns: function(columns) {
+                        let selected = this.selectedNode();
+                        if(selected) {
+                            selected.original.system.columns = columns;
+                        }
+                        return [];
+                    },
+
+                    onDeselected: function(_e, _data) {
+                        linePropsColumn.addClass('d-none');
+                    },
+
+                    onSelected: function(_e, _data) {
+                        let selected = this.selectedNode();
+
+                        if(!(selected && selected.original && selected.original.system && selected.original.system.type === 'stat_tree_line')) return;
+
+                        console.log('ALIX',selected.original.system)
+
+                        linePropsColumn.removeClass('d-none');
+                        linePropsColumn.find('[line-label]').text(selected.original.system.displayLabel);
+
+                        // fill args
+                        let tbody = linePropsColumn.find('[line-args]');
+                        let clauses = selected.original.system.clauses;
+                        tbody.empty();
+                        if(clauses && clauses.length) {
+                            for (let j = 0; j < clauses.length; j++) {
+                                let edit = j === clauses.length - 1;
+                                let args = clauses[j].args ? clauses[j].args : [];
+                                $('<tr/>').addClass(edit ? '' : 'opacity-60 parent-arg').append($('<td/>').attr('colspan', 3).addClass('font-weight-bold text-sm ' + (edit ? '' : 'text-secondary')).text((j+1) + '. ' + clauses[j].clause_label)).appendTo(tbody);
+                                if(!args.length) {
+                                    $('<tr/>').addClass(edit ? '' : 'opacity-60 parent-arg').append($('<td/>').attr('colspan', 3).addClass('pl-3 text-secondary text-sm').text('No args')).appendTo(tbody);
+                                }
+                                else {
+                                    for (let i = 0; i < args.length; i++) {
+                                        $('<tr/>')
+                                            .addClass(edit ? '' : 'opacity-60 parent-arg')
+                                            .append($('<td/>').addClass('pl-3').html(args[i].arg_text + ' <span class="text-secondary text-sm">(' + args[i].field_type + ')</span>'))
+                                            .append($('<td/>').text(args[i].default_value).append(edit ? '<a href="#" class="edit-arg-value ml-2" data-uid="' + args[i].uid + '"><i class="fa fa-edit text-primary text-sm on-hover-opaque"></i></a>': ''))
+                                            .append($('<td/>').text(args[i].access_level).append(edit ? '<a href="#" class="edit-arg-access-level ml-2" data-uid="' + args[i].uid + '"><i class="fa fa-edit text-primary text-sm on-hover-opaque"></i></a>': ''))
+                                            .appendTo(tbody);
                                     }
                                 }
-                            },
-                        },
-                        "plugins": [
-                            "wholerow",
-                            "dnd",
-                            "contextmenu",
-                            "state"
-                        ]
-                    });
+                            }
+                        }
 
-                $('#clauses-view-{{$statTree->id}}')
-                    /*.on('changed.jstree', generateTextRepresentation)
-                    .on('move_node.jstree', generateTextRepresentation)
-                    .on('copy_node.jstree', generateTextRepresentation)*/
-                    .jstree({
-                        "core": {
-                            "multiple": false,
-                            "animation": 0
-                        },
-                        "dnd": {
-                            "is_draggable": function(_node, _e) {
-                                return $('#' + _node[0].id).find('>a.jstree-anchor>.clause').length === 1;
+                        // fill columns
+                        tbody = linePropsColumn.find('[line-columns]');
+                        let columns = StatTree.getSelectedNodeColumns();
+                        tbody.empty();
+                        if(columns && columns.length) {
+                            for (let i = 0; i < columns.length; i++) {
+                                $('<tr/>')
+                                    .append($('<td/>').text(i + 1))
+                                    .append($('<td/>').text(columns[i].label))
+                                    .append($('<td/>').text(columns[i].display_key))
+                                    .append(
+                                        $('<td/>')
+                                            .addClass('text-right text-nowrap')
+                                            .append(i > 0 ? '<a href="#" class="move-column-up mr-2" data-index="' + i + '"><i class="fa fa-arrow-up text-primary on-hover-opaque"></i></a>': '')
+                                            .append(i < columns.length - 1 ? '<a href="#" class="move-column-down mr-2" data-index="' + i + '"><i class="fa fa-arrow-down text-primary on-hover-opaque"></i></a>': '')
+                                            .append('<a href="#" class="remove-column" data-index="' + i + '"><i class="fa fa-trash-alt text-danger on-hover-opaque"></i></a>')
+                                    )
+                                    .appendTo(tbody);
                             }
-                        },
-                        "contextmenu": {
-                            show_at_node: false,
-                            items: function ($node) {
-                                console.log($node);
-                                if($('#' + $node.id).find('>a.jstree-anchor>.clause').length === 1) { // clause
-                                    return {
-                                        "edit": {
-                                            "label": "<span class='text-sm'>Edit Clause</span>",
-                                            "action": function (obj) {
-                                                let id = $(obj.reference).find('>div.clause').attr('data-clause-id');
-                                                $('.edit-clause-moe-' + id).find('a[start]').trigger('click');
-                                            }
-                                        },
-                                        "remove": {
-                                            "label": "<span class='text-sm'>Remove Clause</span>",
-                                            "action": function (obj) {
-                                                let id = $(obj.reference).find('>div.clause').attr('data-clause-id');
-                                                $('.remove-clause-moe-' + id).find('a[start]').trigger('click');
-                                            }
-                                        },
-                                        "add_arg": {
-                                            "label": "<span class='text-sm'>Add Clause Arg</span>",
-                                            "action": function (obj) {
-                                                let id = $(obj.reference).find('>div.clause').attr('data-clause-id');
-                                                $('.add-clause-arg-moe-' + id).find('a[start]').trigger('click');
-                                            }
-                                        }
-                                    }
-                                }
-                            },
-                        },
-                        "plugins": [
-                            "wholerow",
-                            "dnd",
-                            "contextmenu"
-                        ]
-                    });
+                        }
+
+                    },
+                };
 
-                $('#line-properties-column').find('input[stag-suggest][name="displayKey"]')
+                linePropsColumn.find('input[stag-suggest][name="displayKey"]')
                     .off('stag-suggest-selected')
                     .on('stag-suggest-selected', (_e, _input, _data) => {
-                        let columns = getSelectedNodeColumns();
+                        let columns = StatTree.getSelectedNodeColumns();
                         columns.push({
                             label: _data.label,
                             display_key: _data.text
                         });
-                        setSelectedNodeColumns(columns);
-                        onSelected();
-                        autoSaveChanges();
-                        $(_input).val('').focus();
-                        return false;
-                    });
-
-                $('#btn-save-tree')
-                    .off('click')
-                    .on('click', function() {
-                        saveStatTree();
-                        return false;
-                    });
-
-                $('#refresh-counts')
-                    .off('click')
-                    .on('click', function() {
-                        showMask();
-                        $.post("{{ route('practice-management.api.statTree.refreshTreeCountQueries') }}", {
-                            statTreeID: "{{ $statTree->id }}"
-                        }, function (response) {
-                            if(!hasResponseError(response)) {
-                                // fastLoad("{{ route('practice-management.statTrees.view.edit', $statTree) }}");
-                                // need to fix this!
-                                showMask();
-                                window.top.location.reload();
-                            }
-                        }, 'json');
+                        StatTree.setSelectedNodeColumns(columns);
+                        $(_input).val('');
+                        StatTree.save();
                         return false;
                     });
 
                 $(document)
                     .off('click', '.move-column-up')
                     .on('click', '.move-column-up', function() {
-                        let columns = getSelectedNodeColumns();
+                        let columns = StatTree.getSelectedNodeColumns();
                         let index = +($(this).attr('data-index'));
                         if(index > 0) {
                             let x = columns[index - 1];
                             columns[index - 1] = columns[index];
                             columns[index] = x;
                         }
-                        setSelectedNodeColumns(columns);
-                        onSelected();
-                        autoSaveChanges();
+                        StatTree.setSelectedNodeColumns(columns);
+                        StatTree.save();
                         return false;
                     });
 
                 $(document)
                     .off('click', '.move-column-down')
                     .on('click', '.move-column-down', function() {
-                        let columns = getSelectedNodeColumns();
+                        let columns = StatTree.getSelectedNodeColumns();
                         let index = +($(this).attr('data-index'));
                         if(index < columns.length - 1) {
                             let x = columns[index + 1];
                             columns[index + 1] = columns[index];
                             columns[index] = x;
                         }
-                        setSelectedNodeColumns(columns);
-                        onSelected();
-                        autoSaveChanges();
+                        StatTree.setSelectedNodeColumns(columns);
+                        StatTree.save();
                         return false;
                     });
 
                 $(document)
                     .off('click', '.remove-column')
                     .on('click', '.remove-column', function() {
-                        let columns = getSelectedNodeColumns();
+                        let columns = StatTree.getSelectedNodeColumns();
                         columns.splice(+($(this).attr('data-index')), 1);
-                        setSelectedNodeColumns(columns);
-                        onSelected();
-                        autoSaveChanges();
+                        StatTree.setSelectedNodeColumns(columns);
+                        StatTree.save();
                         return false;
                     });
 
@@ -390,10 +681,43 @@
                         return false;
                     });
 
+                $(document)
+                    .off('input change paste', '.frm-clause-add-edit input[name="question"], .frm-clause-add-edit input[name="answer"]')
+                    .on('input change paste', '.frm-clause-add-edit input[name="question"], .frm-clause-add-edit input[name="answer"]', function() {
+                        let form = $(this).closest('.frm-clause-add-edit');
+                        let label = $.trim(form.find('input[name="question"]').val()) + ' ' +
+                            $.trim(form.find('input[name="answer"]').val());
+                        form.find('input[name="label"]').val(label);
+                    });
+
+                $('#refresh-counts')
+                    .off('click')
+                    .on('click', function() {
+                        showMask();
+                        $.post("{{ route('practice-management.api.statTree.refreshTreeCountQueries') }}", {
+                            statTreeID: "{{ $statTree->id }}"
+                        }, function (response) {
+                            if(!hasResponseError(response)) {
+                                StatTree.load();
+                            }
+                        }, 'json').then(hideMask);
+                        return false;
+                    });
+
+                ClausesTree.load();
+                StatTree.load();
                 initMoes();
 
+                addMCHook('reloadClausesTree', function() {
+                    ClausesTree.load();
+                });
+
+                addMCHook('reloadStatTree', function() {
+                    StatTree.load();
+                });
+
             }
-            addMCInitializer('stat-tree-edit-page', init, '#statTreeEdit')
+            addMCInitializer('stat-tree-edit-page', init, '#statTreeEdit');
         }).call(window);
     </script>
 

+ 3 - 1
resources/views/app/stat-tree/tree-edit-v2.blade.php

@@ -52,7 +52,9 @@ if (!function_exists('renderStatTreeLineNodeEdit_Moes')) {
         <?php
     }
 }
-$statTree = \App\Models\StatTree::where('slug', $slug)->first();
+if(!@$statTree && !!@$slug) {
+    $statTree = \App\Models\StatTree::where('slug', $slug)->first();
+}
 ?>
 @if(@$statTree)
     <div class="d-flex align-items-baseline mb-2">

+ 3 - 0
routes/web.php

@@ -319,6 +319,8 @@ Route::middleware('pro.auth')->group(function () {
             Route::name('view.')->prefix('view/{statTree}')->group(function () {
                 Route::get('', 'StatTreeController@dashboard2')->name('dashboard');
                 Route::get('edit', 'StatTreeController@edit')->name('edit');
+                Route::get('clausesJSON', 'StatTreeController@clausesJSON')->name('clausesJSON');
+                Route::get('linesJSON', 'StatTreeController@linesJSON')->name('linesJSON');
 
                 Route::get('old', 'StatTreeController@dashboard')->name('dashboard2');
             });
@@ -356,6 +358,7 @@ Route::middleware('pro.auth')->group(function () {
                 Route::post('update-basic', 'StatTreeController@updateBasic')->name('updateBasic');
                 Route::post('refresh-count', 'StatTreeController@refreshCount')->name('refreshCount');
                 Route::post('replace-all-lines', 'StatTreeController@replaceAllLines')->name('replaceAllLines');
+                Route::post('replace-all-lines-json', 'StatTreeController@replaceAllLinesJSON')->name('replaceAllLinesJSON');
                 Route::post('refresh-tree-count-queries', 'StatTreeLineController@refreshTreeCountQueries')->name('refreshTreeCountQueries');
             });