Commit 705a7487 authored by Alessandro Belli's avatar Alessandro Belli
Browse files

Bugfixing + action table

* added action table and model
* now when a user delete a study or an interview is registered and you can see it in the admin dashboard
* bugfixed firefox view: when you create a token, now the div has 0 height and add it when it gets the image
* the scrollbar is always there, on windows.
parent aaed4a0b
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Symfony2PluginSettings">
<option name="directoryToWeb" value="public" />
<option name="pluginEnabled" value="true" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="SCSS" />
</project>
\ No newline at end of file
......@@ -14,4 +14,7 @@ class Action extends Model
{
return $this->belongsTo(\App\User::class);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ActionController extends Controller
{
public function store(Request $request)
{
$action = new Action();
$action->name = $request->name;
$action->description = $request->description;
$action->url = $request->url();
$action->user = auth()->user()->id;
$action->time = date("Y-m-d H:i:s");
if($action->save()){
return true;
}
}
}
......@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Role;
use App\Action;
use App\User;
use App\Study;
use App\Interview;
......@@ -23,7 +24,8 @@ class AdminController extends Controller
$data['usercount'] = User::all()->count();
$data['studiescount'] = Study::all()->count();
$data['interviewcount'] = Interview::all()->count();
$data['exceptions'] = [];
$data['actions'] = Action::with('user')->get();
$data['actionscount'] = Action::all()->count();
return view('admin.dashboard', $data);
}
......
......@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use DB;
use Auth;
use Exception;
use File;
use Helper;
use Storage;
......@@ -239,16 +240,17 @@ class InterviewController extends Controller
}
/**
* @param Study $study
* @param Interview $interview
* @param Request $request
* @return ResponseFactory|Response
* @throws Exception
*/
public function destroy(Interview $interview)
public function destroy(Interview $interview, Request $request)
{
if (! auth()->user()->hasRole(['superadministrator', 'supervisor'])) {
return response()->json('You cannot delete this interview', 403);
}
$id = $interview->id;
$interview->answers()->detach();
$interview->answers()->delete();
$interview->tokens()->detach();
......@@ -256,6 +258,7 @@ class InterviewController extends Controller
$interview->delete();
File::delete($interview->sorting_screenshot);
$interview->delete();
auth()->user()->addAction('delete interview',$request->url(),'user deleted interview with id '.$id );
return response()->json('interview deleted', 200);
}
......
......@@ -26,7 +26,7 @@ use Illuminate\Auth\Access\AuthorizationException;
class StudyController extends Controller
{
public function create()
public function create(Request $request)
{
// if(!Auth::user()->cancreatestudies())abort(403,'You cannot create studies');
$data['isedit'] = 'false';
......@@ -141,8 +141,9 @@ class StudyController extends Controller
* @return ResponseFactory|Response
* @throws Exception
*/
public function destroy(Study $study)
public function destroy(Study $study,Request $request)
{
if (! auth()->user()->hasRole(['superadministrator', 'supervisor'])) {
abort(403, 'You are not allowed to edit this study');
}
......@@ -156,9 +157,11 @@ class StudyController extends Controller
$interview->delete();
}
$study->interviews()->delete();
$name=$study->name;
$study->delete();
auth()->user()->addAction('delete study',$request->url(),'user deleted study '.$name);
return response('study deleted', 200);
}
......
......@@ -2,13 +2,12 @@
namespace App;
use DB;
use Auth;
use Illuminate\Notifications\Notifiable;
use Laratrust\Traits\LaratrustUserTrait;
use DB;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laratrust\Traits\LaratrustUserTrait;
class User extends Authenticatable
{
......@@ -76,4 +75,22 @@ class User extends Authenticatable
return $canCreateUsers;
}
public function addAction($name, $url, $description = "")
{
$action = new Action();
$action->name = $name;
$action->description = $description;
$action->url = $url;
$action->user_id = auth()->user()->id;
$action->time = date("Y-m-d H:i:s");
$action->save();
return $action;
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateActionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('actions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 255);
$table->text('description')->nullable();
$table->text('url');
$table->integer('user_id')->nullable()->unsigned()->references('id')->on('token')->onDelete('cascade');
$table->datetime('time');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('actions');
}
}
......@@ -44476,6 +44476,13 @@ a {
position: relative;
}
 
.token-img-newtokenInterview {
overflow: auto;
/* adds scrollbars */
max-width: 200px;
position: relative;
}
.radio + .radio {
margin-left: 0em;
}
This diff is collapsed.
{
"/js/app.js": "/js/app.js?id=7c4c3af9fa5d5add3483",
"/css/app.css": "/css/app.css?id=66b75ad2079384b1a4e8",
"/js/app.js": "/js/app.js?id=7c4c5d13336bf0477b12",
"/css/app.css": "/css/app.css?id=e03343dea38cd56c41a3",
"/css/app_dompdf.css": "/css/app_dompdf.css?id=b5406bccc2b11a4f721f",
"/js/manifest.js": "/js/manifest.js?id=41f053ba9a94d81b39f8",
"/js/vendor.js": "/js/vendor.js?id=dced7c5443f90a5803fd"
......
......@@ -39,6 +39,7 @@ Vue.component('consultsorting', require('./components/consultsorting.vue').defau
Vue.component('userpart', require('./components/userpart.vue').default);
Vue.component('interview-list', require('./components/interviewlist.vue').default);
Vue.component('new-token', require('./components/newtokenmodal.vue').default);
Vue.component('action-table', require('./components/actiontable.vue').default);
Vue.config.devtools = true;
Vue.config.debug = true;
......
<template>
<section>
<b-table
:data="isEmpty ? [] : actions"
bordered
narrowed
:default-sort-direction="defaultSortDirection"
:sort-icon="sortIcon"
:sort-icon-size="sortIconSize"
:row-class="(row, index) => row.description.includes('delete') && 'bg-red-500 text-gray-300 hover:bg-red-700'"
>
<template slot-scope="props">
<b-table-column field="id" label="ID" width="40" numeric>
{{ props.row.id }}
</b-table-column>
<b-table-column field="email" label="Author" sortable>
{{ props.row.user.email }}
</b-table-column>
<b-table-column field="name" label="Name" sortable>
{{ props.row.name }}
</b-table-column>
<b-table-column field="description" label="Description" sortable>
{{ props.row.description }}
</b-table-column>
<b-table-column field="url" label="Url" sortable>
{{ props.row.url }}
</b-table-column>
<b-table-column field="date" label="Time" centered sortable>
<span class="tag is-success">
{{ new Date(props.row.time).toLocaleString() }}
</span>
</b-table-column>
</template>
<template slot="empty">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>No Actions.</p>
</div>
</section>
</template>
</b-table>
</section>
</template>
<script>
export default {
name: "actiontable",
props: ['actions'],
data() {
return {
sortIcon: 'arrow-up',
sortIconSize: 'is-small',
defaultSortDirection: 'asc',
}
}
}
</script>
<style scoped>
</style>
<template>
<section>
<div class="create-token absolute w-full" style="left:85%;top:5%;z-index: 1000;" >
<div class="create-token absolute w-full" style="left:85%;top:5%;z-index: 1000;">
<h2 class="block" style="width:30%;">Create own Token.</h2>
<a href="#" class="border-2 border-solid border-black p-6 w-2 h-2 content-center "
style="display: inline-table;"
......@@ -13,68 +13,77 @@
</a>
</div>
<b-modal :active.sync="isComponentModalActive" has-modal-card>
<b-progress v-if="this.$store.state.newinterview.uploadProgress > 1" :value="this.$store.state.newinterview.uploadProgress" show-value format="percent"></b-progress>
<modal-form></modal-form>
<b-modal :active.sync="isComponentModalActive" has-modal-card class="overflow-scroll">
<b-progress v-if="this.$store.state.newinterview.uploadProgress > 1"
:value="this.$store.state.newinterview.uploadProgress" show-value format="percent"></b-progress>
<modal-form>
</modal-form>
</b-modal>
</section>
</template>
<style scoped>
img[src=""] { content:url("data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); }
img {
padding: 100px;
}
</style>
<script>
const ModalForm = {
template: `
<form enctype="multipart/form-data">
<div class="modal-card" style="width: auto">
<header class="modal-card-head">
<p class="modal-card-title">Create new token</p>
</header>
<section class="modal-card-body">
<div class="token-img m-auto w-auto">
<img id="tokenImage" src="" alt=""
style=" height: 100%;width: 100%;vertical-align: bottom; margin:auto;">
</div>
<div class="card-content">
<label class="label">Token name</label>
<input
class="input bg-gray-200 focus:bg-white border-transparent focus:border-blue-400"
type="text" v-model="newinterview.newtoken.name" />
<label class="label mt-5">Preset tokens</label>
<v-select :options="newinterview.imagepreset" label="basename" v-model="newinterview.newtoken.file"
>
<template slot="label" slot-scope="label">
{ option.basename }}
</template>
<template slot="option" slot-scope="option">
<img :src="option.dirname"
style="max-width: 25px;padding: 0px;margin: 0 auto;display:inline;"/>
<span class="fa" :value="option.dirname"></span>
{{ option.basename }}
</template>
</v-select>
</div>
<b-field class="file block has-text-centered">
<b-upload v-model="newinterview.newtoken.file"
accept="image/*">
<a class="button is-danger">
<div class="md:text-sm">Click to upload</div>
</a>
<br>
</b-upload>
</b-field>
</section>
<footer class="modal-card-foot">
<a :disabled="newinterview.newtoken.name == null || newinterview.newtoken.name == ''" :class="newinterview.newtoken.name != null || newinterview.newtoken.name == ''? 'button is-primary text-2xl m-auto w-auto': 'button is-primary text-2xl opacity-50 cursor-not-allowed m-auto w-auto'"
@click.prevent="saveToken"
>Save</a>
</footer>
</div>
</form>
`,
<form enctype="multipart/form-data">
<div class="modal-card" style="width: auto">
<header class="modal-card-head">
<p class="modal-card-title">Create new token</p>
</header>
<section class="modal-card-body modal-newtoken">
<div :class="Object.keys(newinterview.newtoken).length !== 0 ? 'token-img-newtokenInterview m-auto w-auto h-auto' : 'token-img-newtokenInterview m-auto'" >
<img id="tokenImage" :class="Object.keys(newinterview.newtoken).length !== 0 ? 'w-full h-full' : ''" src="" alt=""
style="vertical-align: bottom; ">
</div>
<div class="card-content">
<label class="label">Token name</label>
<input
class="input bg-gray-200 focus:bg-white border-transparent focus:border-blue-400"
type="text" v-model="newinterview.newtoken.name"/>
<label class="label mt-5">Preset tokens</label>
<v-select :options="newinterview.imagepreset" label="basename" v-model="newinterview.newtoken.file"
>
<template slot="label" slot-scope="label">
{ option.basename }}
</template>
<template slot="option" slot-scope="option">
<img :src="option.dirname"
style="max-width: 25px;padding: 0px;margin: 0 auto;display:inline;"
/>
<span class="fa" :value="option.dirname"></span>
{{ option.basename }}
</template>
</v-select>
</div>
<b-field class="file block has-text-centered">
<b-upload v-model="newinterview.newtoken.file"
accept="image/*">
<a class="button is-danger">
<div class="md:text-sm">Click to upload</div>
</a>
<br>
</b-upload>
</b-field>
</section>
<footer class="modal-card-foot">
<a :disabled="newinterview.newtoken.name == null || newinterview.newtoken.name == ''"
:class="newinterview.newtoken.name != null || newinterview.newtoken.name == ''? 'button is-primary text-2xl m-auto w-auto': 'button is-primary text-2xl opacity-50 cursor-not-allowed m-auto w-auto'"
@click.prevent="saveToken"
>Save</a>
</footer>
</div>
</form>
`,
computed: {
...mapState(['newinterview'])
},
......@@ -234,6 +243,7 @@
}
}
}
import {mapState} from 'vuex';
......@@ -261,3 +271,4 @@
methods: {}
}
</script>
......@@ -71,6 +71,12 @@ height: 100%;
max-width: 200px;
position: relative;
}
.token-img-newtokenInterview{
overflow: auto; /* adds scrollbars */
max-width: 200px;
position: relative;
}
.radio + .radio {
margin-left: 0em; }
......
@extends('admin.layout')
@extends('admin.layout')
@section('content')
@section('content')
<section class="hero is-info welcome is-small">
<div class="hero-body">
<div class="container">
<h1 class="title">
Hello, {{$user->email}}.
</h1>
<h2 class="subtitle">
I hope you are having a great day!
</h2>
</div>
</div>
</section>
<section class="info-tiles">
<div class="tile is-ancestor has-text-centered">
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{$usercount}}</p>
<p class="subtitle">Users</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{$studiescount}}</p>
<p class="subtitle">Studies</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{$interviewcount}}</p>
<p class="subtitle">Interviews</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title"></p>
<p class="subtitle">Exceptions</p>
</article>
</div>
</div>
</section>
<div class="columns">
<div class="column">
<div class="card events-card">
<header class="card-header">
<p class="card-header-title">
Exceptions
</p>
<a href="#" class="card-header-icon" aria-label="more options">
<section class="hero is-info welcome is-small">
<div class="hero-body">
<div class="container">
<h1 class="title">
Hello, {{$user->email}}.
</h1>
<h2 class="subtitle">
I hope you are having a great day!
</h2>
</div>
</div>
</section>
<section class="info-tiles">
<div class="tile is-ancestor has-text-centered">
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{$usercount}}</p>
<p class="subtitle">Users</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{$studiescount}}</p>
<p class="subtitle">Studies</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{$interviewcount}}</p>
<p class="subtitle">Interviews</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{$actionscount}}</p>
<p class="subtitle">Actions</p>
</article>
</div>
</div>
</section>
<div class="columns">
<div class="column">
<div class="card events-card">
<header class="card-header">
<p class="card-header-title">
Actions
</p>
<a href="#" class="card-header-icon" aria-label="more options">
<span class="icon">
<i class="fa fa-angle-down" aria-hidden="true"></i>
</span>
</a>
</header>
<div class="card-table">
<div class="content">
<table class="table is-striped">
<tbody>
@forelse($exceptions as $e)
<tr>
<td>
<b-collapse :open="false" class="card" aria-id="contentIdForA11y3">
<div
slot="trigger"
class="card-header"