ソースを参照

Added permissions

jquino 5 年 前
コミット
9fe3623b0b

+ 5
- 1
app/Course.php ファイルの表示

@@ -19,10 +19,14 @@ class Course extends Model
19 19
 
20 20
     public $timestamps = false;
21 21
 
22
-    public function dept() {
22
+    public function department() {
23 23
         return $this->belongsTo(Department::class);
24 24
     }
25 25
 
26
+    public function faculty() {
27
+        return $this->department->faculty();
28
+    }
29
+
26 30
     public function semesters() {
27 31
         return $this->belongsToMany(Semester::class, 'sections');
28 32
     }

+ 0
- 1
app/Department.php ファイルの表示

@@ -8,7 +8,6 @@ class Department extends Model
8 8
 {
9 9
     public $timestamps = false;
10 10
 
11
-
12 11
     public function courses() {
13 12
         return $this->hasMany(Course::class);
14 13
     }

+ 14
- 3
app/Http/Controllers/CourseController.php ファイルの表示

@@ -3,10 +3,13 @@
3 3
 namespace App\Http\Controllers;
4 4
 
5 5
 use App\Course;
6
+use App\Permission;
6 7
 use App\Semester;
7 8
 use App\User;
8 9
 use Illuminate\Http\Request;
9 10
 use Illuminate\Support\Facades\Auth;
11
+use Illuminate\Support\Facades\DB;
12
+use Illuminate\Support\Facades\Gate;
10 13
 use Illuminate\Support\Facades\Redirect;
11 14
 use Illuminate\Support\Facades\Storage;
12 15
 
@@ -19,10 +22,18 @@ class CourseController extends Controller
19 22
      */
20 23
     public function index(Request $request)
21 24
     {
22
-        // if ($request->session()->has('department'))
23
-        // $courses = Course::whereIn('dept_id')
25
+
26
+        $courses = Course::with('sections:id,course_id,semester_code')->select('courses.id', 'courses.code', 'courses.title', 'courses.syllabus');
27
+        // Filter courses by faculty or id
28
+        if ($request->session()->get('filter') === 'f') {
29
+            $courses->join('departments', 'courses.dept_id', '=', 'departments.id')->where('faculty_id', '=', $request->session()->get('faculty'));
30
+        } else if ($request->session()->get('filter') === 'd') {
31
+            $courses->where('dept_id', '=', $request->session()->get('department'));
32
+        }
33
+        // dd($courses->toSql());
34
+        // dd($courses->get());
24 35
         return view('courses.index', [
25
-            'courses' => Course::where('dept_id', '=', $request->session()->get('department'))->with('sections:id,course_id,semester_code')->get()->sort('cmpCourseCode'),
36
+            'courses' => $courses->get()->sort('cmpCourseCode'),
26 37
             'semesters' => Semester::orderBy('code', 'asc')->get(),
27 38
         ]);
28 39
     }

+ 2
- 4
app/Http/Controllers/DashboardController.php ファイルの表示

@@ -30,7 +30,7 @@ class DashboardController extends Controller
30 30
      */
31 31
     public function index()
32 32
     {
33
-        return view('dashboard', [
33
+        return view('dashboard.index', [
34 34
             'professors' => Professor::orderBy('last_name')->get(),
35 35
             'semesters' => Semester::orderBy('code')->get()
36 36
         ]);
@@ -133,8 +133,6 @@ class DashboardController extends Controller
133 133
     /**
134 134
      */
135 135
     public function addUser(Request $request) {
136
-        $data = $request->validate(['email' => ['required', 'email', 'regex:/.+@upr\.edu$/']]);
137
-        User::create($data);
138
-        return redirect()->back();
136
+        return view('dashboard.user');
139 137
     }
140 138
 }

+ 8
- 1
app/Http/Controllers/ProfessorController.php ファイルの表示

@@ -17,8 +17,15 @@ class ProfessorController extends Controller
17 17
     public function index(Request $request)
18 18
     {
19 19
         // dd(Professor::where('dept_id', '=', 1)->with(['sections:semester_code,credits,student_count', 'semesters:semester_code,admin_load,investigative_load'])->orderBy('professors.last_name')->get());
20
+
21
+        $professors = Professor::with(['sections:semester_code,credits,student_count', 'semesters:semester_code,admin_load,investigative_load,other'])->select('professors.id', 'first_name', 'last_name');
22
+        if ($request->session()->get('filter') === 'f') {
23
+            $professors->join('departments', 'professors.dept_id', '=', 'departments.id')->where('faculty_id', '=', $request->session()->get('faculty'));
24
+        } else if ($request->session()->get('filter') === 'd') {
25
+            $professors->where('dept_id', '=', $request->session()->get('department'));
26
+        }
20 27
         return view('professors.index', [
21
-            'professors' => Professor::where('dept_id', '=', $request->session()->get('department'))->with(['sections:semester_code,credits,student_count', 'semesters:semester_code,admin_load,investigative_load,other'])->orderBy('professors.last_name')->get(),
28
+            'professors' => $professors->orderBy('professors.last_name')->get(),
22 29
             'semesters' => Semester::orderBy('semesters.code', 'asc')->get(),
23 30
         ]);
24 31
     }

+ 165
- 0
app/Http/Controllers/UserController.php ファイルの表示

@@ -0,0 +1,165 @@
1
+<?php
2
+
3
+namespace App\Http\Controllers;
4
+
5
+use App\Department;
6
+use App\Faculty;
7
+use App\User;
8
+use Exception;
9
+use Illuminate\Http\Request;
10
+use Illuminate\Support\Facades\Auth;
11
+use Illuminate\Support\Facades\DB;
12
+use Illuminate\Support\Facades\Gate;
13
+
14
+class UserController extends Controller
15
+{
16
+    /**
17
+     * Display a listing of the resource.
18
+     *
19
+     * @return \Illuminate\Http\Response
20
+     */
21
+    public function index()
22
+    {
23
+        //
24
+    }
25
+
26
+    /**
27
+     * Show the form for creating a new resource.
28
+     *
29
+     * @return \Illuminate\Http\Response
30
+     */
31
+    public function create()
32
+    {
33
+        // DB::enableQueryLog();
34
+        $user = Auth::user()->loadMissing(['faculties.departments', 'departments']);
35
+        if ($user->is_admin) {
36
+            $faculties = Faculty::all();
37
+            $departments = Department::all();
38
+        } else {
39
+            $faculties = $user->faculties;
40
+            $departments = $user->departments->keyBy('id');
41
+            foreach($faculties as $faculty) {
42
+                $departments = $departments->union($faculty->departments->keyBy('id'));
43
+            }
44
+        }
45
+        return view('dashboard.register', compact('faculties', 'departments'));
46
+
47
+    }
48
+
49
+    /**
50
+     * Store a newly created resource in storage.
51
+     *
52
+     * @param  \Illuminate\Http\Request  $request
53
+     * @return \Illuminate\Http\Response
54
+     */
55
+    public function store(Request $request)
56
+    {
57
+        // dump($request);
58
+        $userData = $request->validate([
59
+            'email' => ['required', 'email', 'regex:/.+@upr\.edu$/'],
60
+        ]);
61
+        // TODO: Uncomment when added_by column is added to table
62
+        $userData['added_by'] = Auth::user()->id;
63
+        try {
64
+            if (User::where('email', '=', $userData['email'])->get()->isNotEmpty()) {
65
+                throw new Exception('User with that email already exists.');
66
+            }
67
+            $user = User::create($userData);
68
+
69
+        } catch (Exception $e) {
70
+            return redirect()->back()->withErrors(['Failed to add user.', $e->getMessage()]);
71
+        }
72
+
73
+        $permissionData = $request->validate([
74
+            'departments'   => ['nullable', 'array'],
75
+            'departments.*' => ['exists:departments,id'],
76
+            'faculties'     => ['nullable', 'array'],
77
+            'faculties.*'   => ['exists:faculties,id'],
78
+            'admin'         => ['nullable', 'boolean'],
79
+        ]);
80
+
81
+
82
+        // dd($permissionData);
83
+        if (isset($permissionData['departments'])) {
84
+            foreach($permissionData['departments'] as $department_id) {
85
+                if (Gate::allows('add-permission', [1, $department_id])) {
86
+                    $user->permissions()->firstOrCreate([
87
+                        'level'         => 1,
88
+                        'division_id'   => $department_id,
89
+                    ]);
90
+                } else {
91
+                    return redirect()->back()->withErrors('You do not have permission to department ' . Department::find($department_id)->title . '.');
92
+                }
93
+            }
94
+        }
95
+        if (isset($permissionData['faculties'])) {
96
+            foreach($permissionData['faculties'] as $faculty_id) {
97
+                if (Gate::allows('add-permission', [2, $faculty_id])) {
98
+                    $user->permissions()->firstOrCreate([
99
+                        'level'         => 2,
100
+                        'division_id'   => $faculty_id,
101
+                    ]);
102
+                } else {
103
+                    return redirect()->back()->withErrors('You do not have permission to faculty ' . Faculty::find($faculty_id)->name . '.');
104
+                }
105
+            }
106
+        }
107
+        if (isset($permissionData['admin']) && $permissionData['admin']) {
108
+            if (Gate::allows('add-permission', [3, 0])) {
109
+                $user->permissions()->firstOrCreate([
110
+                    'level'         => 3,
111
+                    'division_id'   => 0,
112
+                ]);
113
+            } else {
114
+                return redirect()->back()->withErrors('You do not have campus-wide permissions.');
115
+            }
116
+        }
117
+
118
+        return redirect('/dashboard');
119
+    }
120
+
121
+    /**
122
+     * Display the specified resource.
123
+     *
124
+     * @param  \App\User  $user
125
+     * @return \Illuminate\Http\Response
126
+     */
127
+    public function show(User $user)
128
+    {
129
+        //
130
+    }
131
+
132
+    /**
133
+     * Show the form for editing the specified resource.
134
+     *
135
+     * @param  \App\User  $user
136
+     * @return \Illuminate\Http\Response
137
+     */
138
+    public function edit(User $user)
139
+    {
140
+        //
141
+    }
142
+
143
+    /**
144
+     * Update the specified resource in storage.
145
+     *
146
+     * @param  \Illuminate\Http\Request  $request
147
+     * @param  \App\User  $user
148
+     * @return \Illuminate\Http\Response
149
+     */
150
+    public function update(Request $request, User $user)
151
+    {
152
+        //
153
+    }
154
+
155
+    /**
156
+     * Remove the specified resource from storage.
157
+     *
158
+     * @param  \App\User  $user
159
+     * @return \Illuminate\Http\Response
160
+     */
161
+    public function destroy(User $user)
162
+    {
163
+        //
164
+    }
165
+}

+ 6
- 6
app/Http/Middleware/SetDept.php ファイルの表示

@@ -16,16 +16,16 @@ class SetDept
16 16
     public function handle($request, Closure $next)
17 17
     {
18 18
         if ($request->has('f')) {
19
-            $data = $request->validate(['filter' => 'regex:/[df]/']);
20
-            $request->session()->put('filter', $data['filter']);
19
+            $data = $request->validate(['f' => 'regex:/[df]/']);
20
+            $request->session()->put('filter', $data['f']);
21 21
         }
22 22
         if ($request->has('fclt')) {
23
-            $data = $request->validate(['setfclt' => 'integer|exists:faculties,id']);
24
-            $request->session()->put('faculty', $data['setfclt']);
23
+            $data = $request->validate(['fclt' => 'integer|exists:faculties,id']);
24
+            $request->session()->put('faculty', $data['fclt']);
25 25
         }
26 26
         if ($request->has('dept')) {
27
-            $data = $request->validate(['setdept' => 'integer|exists:departments,id']);
28
-            $request->session()->put('department', $data['setdept']);
27
+            $data = $request->validate(['dept' => 'integer|exists:departments,id']);
28
+            $request->session()->put('department', $data['dept']);
29 29
         }
30 30
         if (!$request->session()->has('filter')) {
31 31
             $request->session()->put('filter', 'd');

+ 25
- 0
app/Permission.php ファイルの表示

@@ -0,0 +1,25 @@
1
+<?php
2
+
3
+namespace App;
4
+
5
+use Illuminate\Database\Eloquent\Model;
6
+
7
+class Permission extends Model
8
+{
9
+    public $timestamps = false;
10
+
11
+    protected $fillable = [
12
+        'user_id',
13
+        'level',
14
+        'division_id'
15
+    ];
16
+
17
+    public function division() {
18
+        if ($this->level === 1) {
19
+            return $this->belongsTo(Department::class, 'division_id');
20
+        } else if ($this->level === 2) {
21
+            return $this->belongsTo(Faculty::class, 'division_id');
22
+        }
23
+        return null;
24
+    }
25
+}

+ 49
- 0
app/Policies/CoursePolicy.php ファイルの表示

@@ -0,0 +1,49 @@
1
+<?php
2
+
3
+namespace App\Policies;
4
+
5
+use App\User;
6
+use App\Course;
7
+use Illuminate\Auth\Access\HandlesAuthorization;
8
+
9
+class CoursePolicy
10
+{
11
+    use HandlesAuthorization;
12
+
13
+    /**
14
+     * Determine whether the user can view any courses.
15
+     *
16
+     * @param  \App\User  $user
17
+     * @return mixed
18
+     */
19
+    public function viewAny(User $user)
20
+    {
21
+        return true;
22
+    }
23
+
24
+    /**
25
+     * Determine whether the user can update the course.
26
+     *
27
+     * @param  \App\User  $user
28
+     * @param  \App\Course  $course
29
+     * @return mixed
30
+     */
31
+    public function update(User $user, Course $course)
32
+    {
33
+        return $user->departments->where('id', '=', $course->dept_id)->isNotEmpty()
34
+            || $user->faculties->where('id', '=', $course->faculty->id)->isNotEmpty();
35
+    }
36
+
37
+    /**
38
+     * Determine whether the user can delete the course.
39
+     *
40
+     * @param  \App\User  $user
41
+     * @param  \App\Course  $course
42
+     * @return mixed
43
+     */
44
+    public function delete(User $user, Course $course)
45
+    {
46
+        return $user->departments->where('id', '=', $course->dept_id)->isNotEmpty()
47
+            || $user->faculties->where('id', '=', $course->faculty->id)->isNotEmpty();
48
+    }
49
+}

+ 50
- 0
app/Policies/ProfessorPolicy.php ファイルの表示

@@ -0,0 +1,50 @@
1
+<?php
2
+
3
+namespace App\Policies;
4
+
5
+use App\User;
6
+use App\Professor;
7
+use Illuminate\Auth\Access\HandlesAuthorization;
8
+
9
+class ProfessorPolicy
10
+{
11
+    use HandlesAuthorization;
12
+
13
+    /**
14
+     * Determine whether the user can view any professors.
15
+     *
16
+     * @param  \App\User  $user
17
+     * @return mixed
18
+     */
19
+    public function viewAny(User $user)
20
+    {
21
+        return true;
22
+    }
23
+
24
+    /**
25
+     * Determine whether the user can update the professor.
26
+     *
27
+     * @param  \App\User  $user
28
+     * @param  \App\Professor  $professor
29
+     * @return mixed
30
+     */
31
+    public function update(User $user, Professor $professor)
32
+    {
33
+        return $user->departments->where('id', '=', $professor->dept_id)->isNotEmpty()
34
+            || $user->faculties->where('id', '=', $professor->faculty->id)->isNotEmpty();
35
+    }
36
+
37
+    /**
38
+     * Determine whether the user can delete the professor.
39
+     *
40
+     * @param  \App\User  $user
41
+     * @param  \App\Professor  $professor
42
+     * @return mixed
43
+     */
44
+    public function delete(User $user, Professor $professor)
45
+    {
46
+        return $user->departments->where('id', '=', $professor->dept_id)->isNotEmpty()
47
+            || $user->faculties->where('id', '=', $professor->faculty->id)->isNotEmpty();
48
+    }
49
+
50
+}

+ 93
- 0
app/Policies/UserPolicy.php ファイルの表示

@@ -0,0 +1,93 @@
1
+<?php
2
+
3
+namespace App\Policies;
4
+
5
+use App\User;
6
+use Illuminate\Auth\Access\HandlesAuthorization;
7
+
8
+class UserPolicy
9
+{
10
+    use HandlesAuthorization;
11
+    
12
+    /**
13
+     * Determine whether the user can view any models.
14
+     *
15
+     * @param  \App\User  $user
16
+     * @return mixed
17
+     */
18
+    public function viewAny(User $user)
19
+    {
20
+        //
21
+    }
22
+
23
+    /**
24
+     * Determine whether the user can view the model.
25
+     *
26
+     * @param  \App\User  $user
27
+     * @param  \App\User  $model
28
+     * @return mixed
29
+     */
30
+    public function view(User $user, User $model)
31
+    {
32
+        //
33
+    }
34
+
35
+    /**
36
+     * Determine whether the user can create models.
37
+     *
38
+     * @param  \App\User  $user
39
+     * @return mixed
40
+     */
41
+    public function create(User $user)
42
+    {
43
+        //
44
+    }
45
+
46
+    /**
47
+     * Determine whether the user can update the model.
48
+     *
49
+     * @param  \App\User  $user
50
+     * @param  \App\User  $model
51
+     * @return mixed
52
+     */
53
+    public function update(User $user, User $model)
54
+    {
55
+        //
56
+    }
57
+
58
+    /**
59
+     * Determine whether the user can delete the model.
60
+     *
61
+     * @param  \App\User  $user
62
+     * @param  \App\User  $model
63
+     * @return mixed
64
+     */
65
+    public function delete(User $user, User $model)
66
+    {
67
+        //
68
+    }
69
+
70
+    /**
71
+     * Determine whether the user can restore the model.
72
+     *
73
+     * @param  \App\User  $user
74
+     * @param  \App\User  $model
75
+     * @return mixed
76
+     */
77
+    public function restore(User $user, User $model)
78
+    {
79
+        //
80
+    }
81
+
82
+    /**
83
+     * Determine whether the user can permanently delete the model.
84
+     *
85
+     * @param  \App\User  $user
86
+     * @param  \App\User  $model
87
+     * @return mixed
88
+     */
89
+    public function forceDelete(User $user, User $model)
90
+    {
91
+        //
92
+    }
93
+}

+ 7
- 0
app/Professor.php ファイルの表示

@@ -17,6 +17,13 @@ class Professor extends Model
17 17
         'dept_id'
18 18
     ];
19 19
 
20
+    public function department() {
21
+        return $this->belongsTo(Department::class);
22
+    }
23
+
24
+    public function faculty() {
25
+        return $this->department->faculty();
26
+    }
20 27
 
21 28
     public function sections() {
22 29
         return $this->belongsToMany(Section::class)->withPivot('percent', 'schedule', 'eval');

+ 15
- 1
app/Providers/AuthServiceProvider.php ファイルの表示

@@ -2,6 +2,8 @@
2 2
 
3 3
 namespace App\Providers;
4 4
 
5
+use App\Course;
6
+use App\Policies\CoursePolicy;
5 7
 use Illuminate\Support\Facades\Gate;
6 8
 use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
7 9
 
@@ -14,6 +16,7 @@ class AuthServiceProvider extends ServiceProvider
14 16
      */
15 17
     protected $policies = [
16 18
         // 'App\Model' => 'App\Policies\ModelPolicy',
19
+        Course::class => CoursePolicy::class,
17 20
     ];
18 21
 
19 22
     /**
@@ -25,6 +28,17 @@ class AuthServiceProvider extends ServiceProvider
25 28
     {
26 29
         $this->registerPolicies();
27 30
 
28
-        //
31
+        Gate::define('add-permission', function ($user, $level, $division_id) {
32
+            $user->loadMissing(['departments', 'faculties.departments']);
33
+            return $user->is_admin
34
+                || ($level === 1) &&
35
+                    ($user->departments->contains($division_id)
36
+                    || $user->faculties->contains(function ($faculty, $key) use ($division_id) {
37
+                        return $faculty->departments->contains($division_id);
38
+                    }))
39
+                || ($level === 2 && $user->faculties->contains($division_id));
40
+        });
41
+
42
+        // Gate::define('')
29 43
     }
30 44
 }

+ 17
- 1
app/User.php ファイルの表示

@@ -16,7 +16,7 @@ class User extends Authenticatable
16 16
      * @var array
17 17
      */
18 18
     protected $fillable = [
19
-        'name', 'email', 'password', 'google_id',
19
+        'name', 'email', 'password', 'google_id', 'added_by',
20 20
     ];
21 21
 
22 22
     /**
@@ -36,4 +36,20 @@ class User extends Authenticatable
36 36
     protected $casts = [
37 37
         'email_verified_at' => 'datetime',
38 38
     ];
39
+
40
+    public function permissions() {
41
+        return $this->hasMany(Permission::class);
42
+    }
43
+
44
+    public function departments() {
45
+        return $this->belongsToMany(Department::class, 'permissions', 'user_id', 'division_id')->wherePivot('level', '=', 1);
46
+    }
47
+
48
+    public function faculties() {
49
+        return $this->belongsToMany(Faculty::class, 'permissions', 'user_id', 'division_id')->wherePivot('level', '=', 2);
50
+    }
51
+
52
+    public function getIsAdminAttribute() {
53
+        return $this->permissions->where('level', '=', 3)->isNotEmpty();
54
+    }
39 55
 }

+ 3
- 1
database/migrations/2014_10_12_000000_create_users_table.php ファイルの表示

@@ -18,9 +18,11 @@ class CreateUsersTable extends Migration
18 18
             $table->string('email')->unique();
19 19
             $table->string('name')->nullable();
20 20
             $table->string('google_id')->nullable();
21
-            $table->timestamp('email_verified_at')->nullable();
21
+            $table->unsignedBigInteger('added_by')->nullable();
22 22
             $table->rememberToken();
23 23
             $table->timestamps();
24
+
25
+            $table->foreign('added_by')->references('id')->on('users');
24 26
         });
25 27
     }
26 28
 

+ 36
- 0
database/migrations/2019_11_13_185313_create_permissions_table.php ファイルの表示

@@ -0,0 +1,36 @@
1
+<?php
2
+
3
+use Illuminate\Support\Facades\Schema;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Database\Migrations\Migration;
6
+
7
+class CreatePermissionsTable extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     *
12
+     * @return void
13
+     */
14
+    public function up()
15
+    {
16
+        Schema::create('permissions', function (Blueprint $table) {
17
+            $table->bigIncrements('id');
18
+            $table->unsignedBigInteger('user_id');
19
+            $table->unsignedTinyInteger('level');
20
+            $table->unsignedBigInteger('division_id');
21
+
22
+            $table->unique(['user_id', 'level', 'division_id']);
23
+            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
24
+        });
25
+    }
26
+
27
+    /**
28
+     * Reverse the migrations.
29
+     *
30
+     * @return void
31
+     */
32
+    public function down()
33
+    {
34
+        Schema::dropIfExists('permissions');
35
+    }
36
+}

+ 14
- 0
resources/js/app.js ファイルの表示

@@ -1,2 +1,16 @@
1 1
 const mdc = require('material-components-web');
2 2
 mdc.autoInit();
3
+
4
+$("ul.dropdown-menu [data-toggle='dropdown']").on("click", function(event) {
5
+    event.preventDefault();
6
+    event.stopPropagation();
7
+
8
+    $(this).siblings().toggleClass("show");
9
+
10
+    if (!$(this).next().hasClass('show')) {
11
+        $(this).parents('.dropdown-menu').first().find('.show').removeClass("show");
12
+    }
13
+    $(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function(e) {
14
+        $('.dropdown-submenu .show').removeClass("show");
15
+    });
16
+});

+ 68
- 37
resources/sass/app.scss ファイルの表示

@@ -2,25 +2,25 @@
2 2
 // @import url('https://fonts.googleapis.com/css?family=Nunito');
3 3
 
4 4
 // Variables
5
-@import 'variables';
5
+@import "variables";
6 6
 
7 7
 // Bootstrap
8
-@import '~bootstrap/scss/bootstrap';
8
+@import "~bootstrap/scss/bootstrap";
9 9
 
10 10
 // Material Design
11 11
 // @import "material-components-web/material-components-web";
12
-@import '@material/typography/mdc-typography';
13
-@import '@material/layout-grid/mdc-layout-grid';
14
-@import '@material/elevation/mdc-elevation';
15
-@import '@material/button/mdc-button';
16
-@import '@material/icon-button/mdc-icon-button';
17
-@import '@material/checkbox/mdc-checkbox';
18
-@import '@material/card/mdc-card';
19
-@import '@material/select/mdc-select';
12
+@import "@material/typography/mdc-typography";
13
+@import "@material/layout-grid/mdc-layout-grid";
14
+@import "@material/elevation/mdc-elevation";
15
+@import "@material/button/mdc-button";
16
+@import "@material/icon-button/mdc-icon-button";
17
+@import "@material/checkbox/mdc-checkbox";
18
+@import "@material/card/mdc-card";
19
+@import "@material/select/mdc-select";
20 20
 @import "@material/list/mdc-list";
21 21
 @import "@material/textfield/mdc-text-field";
22 22
 @import "@material/textfield/helper-text/mdc-text-field-helper-text";
23
-@import '@material/data-table/mdc-data-table';
23
+@import "@material/data-table/mdc-data-table";
24 24
 
25 25
 body {
26 26
     padding-top: 80px;
@@ -29,13 +29,20 @@ body {
29 29
 
30 30
 /* Rules for sizing the icon. */
31 31
 .material-icons {
32
-    .md-18 { font-size: 18px; }
33
-    .md-24 { font-size: 24px; }
34
-    .md-36 { font-size: 36px; }
35
-    .md-48 { font-size: 48px; }
32
+    .md-18 {
33
+        font-size: 18px;
34
+    }
35
+    .md-24 {
36
+        font-size: 24px;
37
+    }
38
+    .md-36 {
39
+        font-size: 36px;
40
+    }
41
+    .md-48 {
42
+        font-size: 48px;
43
+    }
36 44
 }
37 45
 
38
-
39 46
 .landing-page-grid {
40 47
     // @include mdc-layout-grid-inner('desktop', 10vw, 32px);
41 48
     @media screen and (min-width: map-get($mdc-layout-grid-breakpoints, desktop)) {
@@ -57,29 +64,36 @@ body {
57 64
 
58 65
 // MDC card media
59 66
 #home-button-course {
60
-    background-image: url('/images/course_1312478.png');
67
+    background-image: url("/images/course_1312478.png");
61 68
 }
62 69
 #home-button-professor {
63
-    background-image: url('/images/chalkboard-teacher-solid.png');
70
+    background-image: url("/images/chalkboard-teacher-solid.png");
64 71
 }
65 72
 #home-button-dashboard {
66
-    background-image: url('/images/file-alt-regular.png');
73
+    background-image: url("/images/file-alt-regular.png");
67 74
 }
68 75
 #dash-button-csv {
69
-    background-image: url('/images/file-csv.png');
76
+    background-image: url("/images/file-csv.png");
70 77
 }
71 78
 #dash-button-schedule {
72
-    background-image: url('/images/calendar-alt.png')
79
+    background-image: url("/images/calendar-alt.png");
73 80
 }
74 81
 
75
-
76 82
 /* Rules for using icons as black on a light background. */
77
-.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
78
-.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
83
+.material-icons.md-dark {
84
+    color: rgba(0, 0, 0, 0.54);
85
+}
86
+.material-icons.md-dark.md-inactive {
87
+    color: rgba(0, 0, 0, 0.26);
88
+}
79 89
 
80 90
 /* Rules for using icons as white on a dark background. */
81
-.material-icons.md-light { color: rgba(255, 255, 255, 1); }
82
-.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
91
+.material-icons.md-light {
92
+    color: rgba(255, 255, 255, 1);
93
+}
94
+.material-icons.md-light.md-inactive {
95
+    color: rgba(255, 255, 255, 0.3);
96
+}
83 97
 
84 98
 .center-text {
85 99
     vertical-align: middle;
@@ -91,7 +105,7 @@ body {
91 105
 // Apply to table wrapper
92 106
 .table-fixed-col-head {
93 107
     overflow-y: scroll;
94
-    table thead th{
108
+    table thead th {
95 109
         position: -webkit-sticky;
96 110
         position: sticky;
97 111
         top: 0;
@@ -118,7 +132,6 @@ body {
118 132
     }
119 133
 }
120 134
 
121
-
122 135
 .mdc-data-table {
123 136
     th {
124 137
         font-weight: bold;
@@ -134,7 +147,6 @@ body {
134 147
     font-weight: bold;
135 148
 }
136 149
 
137
-
138 150
 .table--fit-screen {
139 151
     height: 80vh;
140 152
     width: 96vw;
@@ -153,6 +165,24 @@ body {
153 165
     right: 0;
154 166
 }
155 167
 
168
+.dropdown-submenu {
169
+    position: relative;
170
+}
171
+
172
+.dropdown-submenu > a:after {
173
+    content: "\f0da";
174
+    float: right;
175
+    border: none;
176
+    font-family: "FontAwesome";
177
+}
178
+
179
+.dropdown-submenu > .dropdown-menu {
180
+    top: 0;
181
+    left: 100%;
182
+    margin-top: 0px;
183
+    margin-left: 0px;
184
+}
185
+
156 186
 [onclick] {
157 187
     cursor: pointer;
158 188
 }
@@ -162,26 +192,27 @@ body {
162 192
 }
163 193
 
164 194
 h1 {
165
-    @include mdc-typography(headline1)
195
+    @include mdc-typography(headline1);
196
+    font-size: 4.25rem;
166 197
 }
167 198
 h2 {
168
-    @include mdc-typography(headline2)
199
+    @include mdc-typography(headline2);
169 200
 }
170 201
 h3 {
171
-    @include mdc-typography(headline3)
202
+    @include mdc-typography(headline3);
172 203
 }
173 204
 h4 {
174
-    @include mdc-typography(headline4)
205
+    @include mdc-typography(headline4);
175 206
 }
176 207
 h5 {
177
-    @include mdc-typography(headline5)
208
+    @include mdc-typography(headline5);
178 209
 }
179 210
 h6 {
180
-    @include mdc-typography(headline6)
211
+    @include mdc-typography(headline6);
181 212
 }
182 213
 button {
183
-    @include mdc-typography(button)
214
+    @include mdc-typography(button);
184 215
 }
185 216
 p {
186
-    @include mdc-typography(body1)
217
+    @include mdc-typography(body1);
187 218
 }

resources/views/dashboard.blade.php → resources/views/dashboard/index.blade.php ファイルの表示

@@ -54,7 +54,7 @@
54 54
                 </a>
55 55
             </div>
56 56
             <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-2">
57
-                <a data-toggle="modal" data-target="#modal-user-add">
57
+                <a href="{{ route('user.create') }}" style="text-decoration: none; color: inherit;">
58 58
                     <div class="mdc-card">
59 59
                         <div class="mdc-card__primary-action" tabindex="0">
60 60
                             <div id="dash-button-schedule" class="mdc-card__media mdc-card__media--square"></div>

+ 132
- 0
resources/views/dashboard/register.blade.php ファイルの表示

@@ -0,0 +1,132 @@
1
+@extends('layouts.app')
2
+
3
+@section('title', 'Nuevo Usuario | Gerencia Docente')
4
+
5
+@section('content')
6
+<div class="mdc-layout-grid__inner dashboard-grid">
7
+    <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
8
+        <div class="mdc-layout-grid__inner">
9
+            <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12-desktop mdc-layout-grid__cell--span-8-tablet mdc-layout-grid__cell--span-4-phone">
10
+                <h2 class="mdc-typography--heading1">Usuario Nuevo</h2>
11
+            </div>
12
+        </div>
13
+        @if ($errors->any())
14
+            <div class="mdc-layout-grid__inner">
15
+                <div class="alert alert-danger alert-dismissible fade show mdc-layout-grid__cell mdc-layout-grid__cell--span-12" role="alert">
16
+                    <button type="button" class="close" data-dismiss="modal" aria-label="close">
17
+                        <span aria-hidden="true">&times;</span>
18
+                    </button>
19
+                    <ul class="mdc-list">
20
+                        @foreach ($errors->all() as $error)
21
+                            <li class="mcd-list-item"><span class="mdc-list-item__text">{{ $error }}</span></li>
22
+                        @endforeach
23
+                    </ul>
24
+                </div>
25
+            </div>
26
+            <br>
27
+        @endif
28
+        <div class="mdc-layout-grid__inner">
29
+            <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
30
+                <br>
31
+                <form method="POST" action="{{ route('user.store') }}">
32
+                    @csrf
33
+                    <div class="mdc-layout-grid__inner">
34
+                        <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
35
+                            <div class="form-field">
36
+                                <div class="mdc-text-field" data-mdc-auto-init="MDCTextField">
37
+                                    <input type="email" id="email-field" class="mdc-text-field__input" name="email">
38
+                                    <label class="mdc-floating-label" for="email-field">E-mail</label>
39
+                                    <div class="mdc-line-ripple"></div>
40
+                                </div>
41
+                            </div>
42
+                        </div>
43
+                        <hr class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
44
+                        <h3 class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12 mdc-layout-grid__cell--span-4-mobile">
45
+                            Permisos
46
+                        </h3>
47
+                        @if (Auth::user()->is_admin)
48
+                            <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
49
+                                <div class="mdc-form-field">
50
+                                    <div class="mdc-checkbox" data-mdc-auto-init="MDCCheckbox">
51
+                                        <input type="checkbox" class="mdc-checkbox__native-control" id="checkbox-admin" name="admin" value="1">
52
+                                        <div class="mdc-checkbox__background">
53
+                                            <svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
54
+                                                <path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
55
+                                            </svg>
56
+                                            <div class="mdc-checkbox__mixedmark"></div>
57
+                                        </div>
58
+                                        <div class="mdc-checkbox__ripple"></div>
59
+                                    </div>
60
+                                    <label for="checkbox-admin">Nivel recinto</label>
61
+                                </div>
62
+                            </div>
63
+                        @endif
64
+                        @if ($faculties->isNotEmpty())
65
+                            <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-6">
66
+                                <div class="mdc-layout-grid__inner">
67
+                                    <h4 class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">Facultades</h4>
68
+                                    @foreach ($faculties->sortBy('name') as $faculty)
69
+                                        <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-6">
70
+                                            <div class="mdc-form-field">
71
+                                                <div class="mdc-checkbox">
72
+                                                    <input type="checkbox" class="mdc-checkbox__native-control" id="checkbox-f-{{ $loop->index }}" name="faculties[]" value="{{ $faculty->id }}">
73
+                                                    <div class="mdc-checkbox__background">
74
+                                                        <svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
75
+                                                            <path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
76
+                                                        </svg>
77
+                                                        <div class="mdc-checkbox__mixedmark"></div>
78
+                                                    </div>
79
+                                                    <div class="mdc-checkbox__ripple"></div>
80
+                                                </div>
81
+                                                <label for="checkbox-f-{{ $loop->index }}">{{ $faculty->name }}</label>
82
+                                            </div>
83
+                                        </div>
84
+                                    @endforeach
85
+                                </div>
86
+                            </div>
87
+                        @endif
88
+                        @if ($departments->isNotEmpty())
89
+                            <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-6">
90
+                                <div class="mdc-layout-grid__inner">
91
+                                    <h4 class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">Departamentos</h4>
92
+                                    @foreach ($departments->sortBy('title') as $department)
93
+                                        <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-6">
94
+                                            <div class="mdc-form-field">
95
+                                                <div class="mdc-checkbox" data-auto-init="MDCCheckbox">
96
+                                                    <input type="checkbox" class="mdc-checkbox__native-control" id="checkbox-d-{{ $loop->index }}" name="departments[]" value="{{ $department->id }}">
97
+                                                    <div class="mdc-checkbox__background">
98
+                                                        <svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
99
+                                                            <path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
100
+                                                        </svg>
101
+                                                        <div class="mdc-checkbox__mixedmark"></div>
102
+                                                    </div>
103
+                                                    <div class="mdc-checkbox__ripple"></div>
104
+                                                </div>
105
+                                                <label for="checkbox-d-{{ $loop->index }}">{{ $department->title ?: $department->name }}</label>
106
+                                            </div>
107
+                                        </div>
108
+                                    @endforeach
109
+                                </div>
110
+                            </div>
111
+                        @endif
112
+                        <hr class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
113
+                        <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
114
+                            <input type="submit" class="mdc-button mdc-button--raised" value="Añadir usuario">
115
+                        </div>
116
+                    </div>
117
+                </form>
118
+            </div>
119
+        </div>
120
+    </div>
121
+</div>
122
+@endsection
123
+
124
+@section('modals')
125
+    @include('modal.semester')
126
+    @include('modal.semester-clone')
127
+    @include('modal.user-add')
128
+@endsection
129
+
130
+@section('scripts')
131
+    <script type="text/javascript" src="/js/dashboard.js"></script>
132
+@endsection

+ 20
- 6
resources/views/layouts/app.blade.php ファイルの表示

@@ -49,14 +49,28 @@
49 49
                         <a class="nav-link dropdown-toggle mdc-typography--body1" href="#" data-toggle="dropdown">
50 50
                             {{ $department->title ?: $department->name ?? 'Departamento' }} <span class="caret"></span>
51 51
                         </a>
52
-                        <div class="dropdown-menu">
52
+                        <ul class="dropdown-menu">
53 53
                             @foreach (App\Faculty::orderBy('name')->get() as $faculty)
54
-                                <a class="dropdown-item mdc-typography--body1" href="{{ url()->current() }}?setfac={{ $faculty->id }}">{{ $faculty->name }}</a>
55
-                                @foreach ($faculty->departments as $dept)
56
-                                    <a class="dropdown-item mdc-typography--body2" href="{{ url()->current() }}?setdept={{ $dept->id }}" style="padding-left:4em;">{{ $dept->title ?: $dept->name }}</a>
57
-                                @endforeach
54
+                                @if (!$faculty->departments->isEmpty())
55
+                                    <li class="dropdown-submenu">
56
+                                        <a class="dropdown-item dropdown-toggle mdc-typography--body1" href="#" data-toggle="dropdown">{{ $faculty->name }}</a>
57
+                                        <ul class="dropdown-menu">
58
+                                            @foreach ($faculty->departments as $dept)
59
+                                                <li class="mdc-typography--body2">
60
+                                                    <a class="dropdown-item" href="{{ url()->current() }}?f=d&dept={{ $dept->id }}">
61
+                                                        {{ $dept->title ?: $dept->name }}
62
+                                                    </a>
63
+                                                </li>
64
+                                            @endforeach
65
+                                        </ul>
66
+                                    </li>
67
+                                @else
68
+                                    <li>
69
+                                        <a class="dropdown-item" href="{{ url()->current() }}?f=f&fclt={{ $faculty->id }}">{{ $faculty->name }}</a>
70
+                                    </li>
71
+                                @endif
58 72
                             @endforeach
59
-                        </div>
73
+                        </ul>
60 74
                     </li>
61 75
                 </ul>
62 76
 

+ 4
- 1
routes/web.php ファイルの表示

@@ -24,14 +24,17 @@ Route::resource('professor', 'ProfessorController')->except([
24 24
 Route::resource('section', 'SectionController')->only([
25 25
     'store', 'update', 'destroy'
26 26
 ]);
27
+Route::resource('user', 'UserController')->only([
28
+    'create', 'store',
29
+]);
27 30
 
28 31
 // Auth::routes();
29 32
 
30 33
 Route::get('/dashboard', 'DashboardController@index')->name('dashboard');
34
+Route::get('/dashboard/add-user', 'DashboardController@addUser')->name('addUser');
31 35
 Route::post('/dashboard/schedule/{semester}', 'DashboardController@schedule')->name('schedule');
32 36
 Route::post('/dashboard/export/{semester}', 'DashboardController@export')->name('export');
33 37
 Route::post('/dashboard/clone/{semester}', 'DashboardController@cloneSemester')->name('cloneSemester');
34
-Route::post('/dashboard/add-user', 'DashboardController@addUser')->name('addUser');
35 38
 Route::get('/dashboard/export-courses', 'DashboardController@exportCourses')->name('exportCourses');
36 39
 
37 40
 Route::get('/login', 'Auth\LoginController@redirectToProvider')->name('login');