From ba28588e386cf54cb7da4bb06c75167aa156b823 Mon Sep 17 00:00:00 2001 From: Harshil Vora Date: Sat, 18 Oct 2025 02:53:32 -0700 Subject: [PATCH] feat(profile,publications): integrate working Google Scholar import flow and unified faculty profile system --- .env.example | 3 + .gitignore | 2 +- app/Http/Controllers/FacultyController.php | 14 +- .../Controllers/FacultyProfileController.php | 120 ++++++++++++ app/Http/Controllers/ProfileController.php | 60 ------ .../Controllers/PublicationsController.php | 160 ++++++++++++++-- app/Models/FacultyProfile.php | 41 ++++ app/Models/User.php | 20 +- app/Services/ScholarService.php | 67 +++++++ ...create_books_published_table_disabled.php} | 4 +- ..._161236_add_scholar_url_to_users_table.php | 22 +++ ...95531_make_publication_fields_nullable.php | 26 +++ .../create_faculty_profiles_table.php | 45 +++++ resources/views/faculty/profile.blade.php | 14 ++ .../views/layouts/partials/navbar.blade.php | 40 +++- .../views/pages/publications/index.blade.php | 176 +++++++++++++++++- resources/views/profile/edit.blade.php | 120 +++++++++--- resources/views/profile/index.blade.php | 89 +++++++++ routes/web.php | 31 ++- 19 files changed, 915 insertions(+), 139 deletions(-) create mode 100644 app/Http/Controllers/FacultyProfileController.php delete mode 100644 app/Http/Controllers/ProfileController.php create mode 100644 app/Models/FacultyProfile.php create mode 100644 app/Services/ScholarService.php rename database/migrations/{2025_03_31_145615_create_books_published_table.php => 2025_03_31_145615_create_books_published_table_disabled.php} (91%) create mode 100644 database/migrations/2025_10_17_161236_add_scholar_url_to_users_table.php create mode 100644 database/migrations/2025_10_17_195531_make_publication_fields_nullable.php create mode 100644 database/migrations/create_faculty_profiles_table.php create mode 100644 resources/views/faculty/profile.blade.php create mode 100644 resources/views/profile/index.blade.php diff --git a/.env.example b/.env.example index 1d9931c..11a3ffa 100644 --- a/.env.example +++ b/.env.example @@ -64,3 +64,6 @@ AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" + +SERPAPI_KEY=75d51e578a0d0d2ac4cb1d6ae21410567a105d96b8df36411b4772d91beaa45c + diff --git a/.gitignore b/.gitignore index c7cf1fa..887dfa8 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,6 @@ yarn-error.log /auth.json /.fleet /.idea -/.nova +/.novaP /.vscode /.zed diff --git a/app/Http/Controllers/FacultyController.php b/app/Http/Controllers/FacultyController.php index 8ae0a39..d65f24a 100644 --- a/app/Http/Controllers/FacultyController.php +++ b/app/Http/Controllers/FacultyController.php @@ -73,8 +73,14 @@ class FacultyController extends Controller public function viewPublicationsResponses() { - $departments = Department::all(); - return view('pages.publications.index', compact('departments')); + $user = auth()->user(); + + \Log::info('FacultyController: scholar_url = ' . ($user->scholar_url ?? 'NULL')); + + return view('pages.publications.index', [ + 'scholarUrl' => $user->scholar_url, + ]); + } public function BooksPublishedForm() @@ -536,7 +542,7 @@ class FacultyController extends Controller 'offered_by' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date', - 'num_days' => 'required|integer', + 'num_days' => 'required|integer', 'proof' => 'nullable|mimes:jpg,jpeg,png,pdf,doc,docx,zip', ]); @@ -615,7 +621,7 @@ class FacultyController extends Controller } // dd($proofFilePath); - + // Save the response to the database Patent::create([ 'department_id' => auth()->user()->department->id, diff --git a/app/Http/Controllers/FacultyProfileController.php b/app/Http/Controllers/FacultyProfileController.php new file mode 100644 index 0000000..54703cf --- /dev/null +++ b/app/Http/Controllers/FacultyProfileController.php @@ -0,0 +1,120 @@ + $user->id], + [ + 'name' => $user->name ?? '', + 'email' => $user->email ?? '', + 'phone_number' => '', + 'vidhvan_id' => '', + 'google_scholar_id' => '', + 'linkedin_id' => '', + 'designation' => '', + 'salutation' => '', + 'qualification' => '', + 'address' => '', + 'employee_id' => '', + 'department_name' => '', + 'programme_name' => '', + 'date_of_joining' => now()->toDateString(), + ] + ); + + return view('profile.index', compact('user', 'profile')); + } + + /** + * Show the edit form for faculty profile. + */ + public function edit() + { + $user = Auth::user(); + $profile = FacultyProfile::where('user_id', $user->id)->firstOrFail(); + + return view('profile.edit', compact('profile')); + } + + /** + * Update faculty profile. + */ + public function update(Request $request) + { + $user = Auth::user(); + $profile = FacultyProfile::where('user_id', $user->id)->firstOrFail(); + + $validated = $request->validate([ + 'name' => 'required|string|max:255', + 'email' => 'required|email', + 'phone_number' => 'nullable|string|max:20', + 'employee_id' => 'nullable|string|max:255', + 'department_name' => 'nullable|string|max:255', + 'programme_name' => 'nullable|string|max:255', + 'salutation' => 'nullable|string|max:50', + 'designation' => 'nullable|string|max:255', + 'date_of_joining' => 'nullable|date', + 'qualification' => 'nullable|string|max:500', + 'address' => 'nullable|string|max:500', + 'google_scholar_id' => 'nullable|string|max:255', + 'vidhvan_id' => 'nullable|string|max:255', + 'linkedin_id' => 'nullable|string|max:255', + 'profile_photo' => 'nullable|image|max:2048', // 2MB max + ]); + + // Handle profile photo upload + if ($request->hasFile('profile_photo')) { + if ($profile->profile_photo_path && Storage::exists($profile->profile_photo_path)) { + Storage::delete($profile->profile_photo_path); + } + + $validated['profile_photo_path'] = $request->file('profile_photo') + ->store('public/profile_photos'); + } + + // Update profile with validated data + $profile->update([ + 'name' => $validated['name'] ?? $profile->name, + 'email' => $validated['email'] ?? $profile->email, + 'phone_number' => $validated['phone_number'] ?? '', + 'employee_id' => $validated['employee_id'] ?? '', + 'department_name' => $validated['department_name'] ?? '', + 'programme_name' => $validated['programme_name'] ?? '', + 'salutation' => $validated['salutation'] ?? '', + 'designation' => $validated['designation'] ?? '', + 'date_of_joining' => $validated['date_of_joining'] ?? $profile->date_of_joining, + 'qualification' => $validated['qualification'] ?? '', + 'address' => $validated['address'] ?? '', + 'google_scholar_id' => $validated['google_scholar_id'] ?? '', + 'vidhvan_id' => $validated['vidhvan_id'] ?? '', + 'linkedin_id' => $validated['linkedin_id'] ?? '', + 'profile_photo_path' => $validated['profile_photo_path'] ?? $profile->profile_photo_path, + ]); + + // ✅ Update the main user record (used by PublicationsController) + if ($request->filled('google_scholar_id')) { + $user->scholar_url = $request->input('google_scholar_id'); + $user->save(); + } + + // ✅ Refresh session to ensure it reflects the latest Scholar URL immediately + Auth::setUser($user->fresh()); + + return redirect() + ->route('faculty.profile.index') + ->with('success', 'Profile updated successfully.'); + } +} diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php deleted file mode 100644 index a48eb8d..0000000 --- a/app/Http/Controllers/ProfileController.php +++ /dev/null @@ -1,60 +0,0 @@ - $request->user(), - ]); - } - - /** - * Update the user's profile information. - */ - public function update(ProfileUpdateRequest $request): RedirectResponse - { - $request->user()->fill($request->validated()); - - if ($request->user()->isDirty('email')) { - $request->user()->email_verified_at = null; - } - - $request->user()->save(); - - return Redirect::route('profile.edit')->with('status', 'profile-updated'); - } - - /** - * Delete the user's account. - */ - public function destroy(Request $request): RedirectResponse - { - $request->validateWithBag('userDeletion', [ - 'password' => ['required', 'current_password'], - ]); - - $user = $request->user(); - - Auth::logout(); - - $user->delete(); - - $request->session()->invalidate(); - $request->session()->regenerateToken(); - - return Redirect::to('/'); - } -} diff --git a/app/Http/Controllers/PublicationsController.php b/app/Http/Controllers/PublicationsController.php index 2abd6fe..b43d7ee 100644 --- a/app/Http/Controllers/PublicationsController.php +++ b/app/Http/Controllers/PublicationsController.php @@ -6,9 +6,19 @@ use Illuminate\Http\Request; use Yajra\DataTables\Facades\DataTables; use App\Models\Publication; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; +use App\Services\ScholarService; class PublicationsController extends Controller { + public function index() + { + $user = auth()->user(); + $scholarUrl = $user->scholar_url; + + return view('pages.publications.index', compact('scholarUrl')); + } + public function edit($id) { $publication = Publication::findOrFail($id); @@ -23,7 +33,6 @@ class PublicationsController extends Controller { $publication = Publication::findOrFail($id); - // Validate the request data $validated = $request->validate([ 'first_author_name' => 'required|string', 'co_authors' => 'nullable|string', @@ -43,34 +52,27 @@ class PublicationsController extends Controller 'paper_file' => 'nullable|mimes:jpg,jpeg,png,pdf,doc,docx,zip', ]); - // Combine start date and time $startDateTime = date('Y-m-d H:i:s', strtotime("{$validated['start_date']} {$validated['start_time']}")); $endDateTime = date('Y-m-d H:i:s', strtotime("{$validated['end_date']} {$validated['end_time']}")); - // Handle the file upload if a new file is provided if ($request->hasFile('paper_file')) { - // Delete old file if exists if ($publication->paper_file && Storage::disk('public')->exists($publication->paper_file)) { Storage::disk('public')->delete($publication->paper_file); } - // Extract year from start_date $year = date('Y', strtotime($validated['start_date'])); $username = $publication->user->name; $originalName = $request->file('paper_file')->getClientOriginalName(); $fileName = $username . '_' . $originalName; - // Create path structure: year/faculty_name/Publications $folderPath = 'proofs/' . $year . '/' . $username . '/Publications'; - // Store file in the specified path $paperFilePath = $request->file('paper_file')->storeAs($folderPath, $fileName, 'public'); $publication->paper_file = $paperFilePath; } - // Update other fields $publication->first_author_name = $validated['first_author_name']; $publication->co_authors = $validated['co_authors']; $publication->start_date = $startDateTime; @@ -96,7 +98,6 @@ class PublicationsController extends Controller return redirect()->route('coordinator.PublicationsResponses') ->with('status', 'Publication updated successfully'); } else { - // For regular users return redirect()->route('faculty.PublicationsResponses') ->with('status', 'Publication updated successfully'); } @@ -106,7 +107,6 @@ class PublicationsController extends Controller { $publication = Publication::findOrFail($id); - // Delete the file if it exists if ($publication->paper_file && Storage::disk('public')->exists($publication->paper_file)) { Storage::disk('public')->delete($publication->paper_file); } @@ -122,18 +122,14 @@ class PublicationsController extends Controller $isAdmin = $user->role->name === 'Admin'; $isCoordinator = $user->role->name === 'Coordinator'; - // Query based on role if ($isAdmin) { - // Admin sees all records $publications = Publication::with('user', 'department'); } elseif ($isCoordinator) { - // Coordinator sees only their department's records $publications = Publication::with('user', 'department') ->whereHas('user', function ($query) use ($user) { $query->where('department_id', $user->department_id); }); } else { - // Regular users see only their own records $publications = Publication::with('user', 'department') ->where('faculty_id', $user->id); } @@ -166,16 +162,13 @@ class PublicationsController extends Controller ->addColumn('action', function ($publication) { $actions = []; - // View paper_file button for everyone if ($publication->paper_file) { $actions[] = 'View Paper'; } else { $actions[] = 'No Paper'; } - // Edit button with role-appropriate route $userRole = auth()->user()->role->name; - // Determine the appropriate route based on user role if ($userRole === 'Admin') { $editRoute = route('admin.Publications.edit', $publication->id); } elseif ($userRole === 'Coordinator') { @@ -194,4 +187,135 @@ class PublicationsController extends Controller ->rawColumns(['action']) ->make(true); } -} \ No newline at end of file + + /** + * Fetch all publications from user's linked Google Scholar profile. + */ + public function fetchScholar(Request $request) + { + try { + + $user = auth()->user(); + \Log::info('📡 FetchScholar Triggered', ['scholar_url' => $user->scholar_url]); + + + if (!$user || !$user->scholar_url) { + return response()->json([ + 'success' => false, + 'message' => 'No Google Scholar URL found for the logged-in user.' + ], 400); + } + + $scholarService = new \App\Services\ScholarService(); + $publications = $scholarService->fetchPublications($user->scholar_url); + + if (empty($publications)) { + return response()->json([ + 'success' => false, + 'message' => 'No publications found from the provided Scholar profile.' + ]); + } + + return response()->json([ + 'success' => true, + 'count' => count($publications), + 'data' => $publications, + ]); + + } catch (\Throwable $e) { + return response()->json([ + 'success' => false, + 'message' => 'Error fetching publications: '.$e->getMessage(), + ], 500); + } + } + + /** + * Import selected publications (skipping duplicates). + */ + public function importScholar(Request $request) + { + try { + $user = auth()->user(); + $facultyId = $user->id; + + $validated = $request->validate([ + 'publications' => 'required|array', + 'publications.*.title' => 'required|string', + 'publications.*.authors' => 'nullable|string', + 'publications.*.journal' => 'nullable|string', + 'publications.*.year' => 'nullable|string', + 'publications.*.link' => 'nullable|url', + ]); + + $rows = $validated['publications']; + $imported = 0; + $skipped = 0; + + foreach ($rows as $pub) { + $exists = \App\Models\Publication::where('faculty_id', $facultyId) + ->whereRaw('LOWER(title) = ?', [strtolower($pub['title'])]) + ->exists(); + + if ($exists) { + $skipped++; + continue; + } + + // 🔹 Derive year safely (default to current year if blank) + $year = (int)($pub['year'] ?? date('Y')); + $year = ($year < 1900 || $year > date('Y') + 1) ? date('Y') : $year; + + // 🔹 Set start/end to that year's first/last day + $startDate = "{$year}-01-01"; + $endDate = "{$year}-12-31"; + + // 🔍 Smart Tag Detection + $journalLower = strtolower($pub['journal'] ?? ''); + + // Detect peer-reviewed journals + $isPeerReviewed = ( + str_contains($journalLower, 'ieee') || + str_contains($journalLower, 'elsevier') || + str_contains($journalLower, 'springer') || + str_contains($journalLower, 'scopus') || + str_contains($journalLower, 'wiley') || + str_contains($journalLower, 'nature') || + str_contains($journalLower, 'taylor & francis') + ) ? 'yes' : 'no'; + + // Detect Scopus / SCI tags + $isScopus = str_contains($journalLower, 'scopus') ? 'Yes' : 'No'; + $isSci = str_contains($journalLower, 'sci') ? 'Yes' : 'No'; + + \App\Models\Publication::create([ + 'faculty_id' => $facultyId, + 'department_id' => $user->department_id, + 'affiliation' => $pub['journal'] ?? '-', + 'organizing_institute' => null, + 'venue_address' => null, + 'title' => $pub['title'], + 'first_author_name' => $user->name, + 'co_authors' => $pub['authors'] ?? '-', + 'journal_name' => $pub['journal'] ?? '-', + 'start_date' => $startDate, + 'end_date' => $endDate, + 'num_days' => 365, + 'activity_type' => 'Google Scholar', + 'is_peer_reviewed' => $isPeerReviewed, + 'is_scopus_indexed' => $isScopus, + 'is_sci_indexed' => $isSci, + 'external_link' => $pub['link'] ?? null, + ]); + + $imported++; + } + + return response()->json([ + 'message' => "Imported $imported publications, skipped $skipped duplicates." + ]); + } catch (\Throwable $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } +} diff --git a/app/Models/FacultyProfile.php b/app/Models/FacultyProfile.php new file mode 100644 index 0000000..4f4e38a --- /dev/null +++ b/app/Models/FacultyProfile.php @@ -0,0 +1,41 @@ + 'array', + 'date_of_joining' => 'date', + ]; + + // Each profile belongs to one user + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 33b67da..b40f2a3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,21 +2,14 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { - /** @use HasFactory<\Database\Factories\UserFactory> */ use HasFactory, Notifiable; - /** - * The attributes that are mass assignable. - * - * @var list - */ protected $fillable = [ 'name', 'email', @@ -27,24 +20,14 @@ class User extends Authenticatable 'scopus_id', 'mobile_no', 'extension', + 'scholar_url', // ✅ added this line ]; - - /** - * The attributes that should be hidden for serialization. - * - * @var list - */ protected $hidden = [ 'password', 'remember_token', ]; - /** - * Get the attributes that should be cast. - * - * @return array - */ protected function casts(): array { return [ @@ -53,7 +36,6 @@ class User extends Authenticatable ]; } - public function role() { return $this->belongsTo(Role::class); diff --git a/app/Services/ScholarService.php b/app/Services/ScholarService.php new file mode 100644 index 0000000..eee285e --- /dev/null +++ b/app/Services/ScholarService.php @@ -0,0 +1,67 @@ + 'google_scholar_author', + 'author_id' => $profileId, + 'api_key' => $apiKey, + ]); + + if (!$response->successful()) { + \Log::error('Scholar API request failed: ' . $response->status()); + return []; + } + + $json = $response->json(); + + // Support multiple possible structures + $articles = $json['articles'] + ?? $json['publications'] + ?? $json['results'] + ?? []; + + // ✅ Format and return + return collect($articles) + ->map(function ($a) { + return [ + 'title' => $a['title'] ?? $a['name'] ?? null, + 'authors' => $a['authors'] ?? ($a['author'] ?? null), + 'journal' => $a['publication'] ?? $a['journal'] ?? null, + 'year' => $a['year'] ?? ($a['publication_year'] ?? null), + 'link' => $a['link'] ?? $a['citation_link'] ?? null, + ]; + }) + ->toArray(); + + } catch (\Throwable $e) { + \Log::error('ScholarService API error: ' . $e->getMessage()); + return []; + } + } +} diff --git a/database/migrations/2025_03_31_145615_create_books_published_table.php b/database/migrations/2025_03_31_145615_create_books_published_table_disabled.php similarity index 91% rename from database/migrations/2025_03_31_145615_create_books_published_table.php rename to database/migrations/2025_03_31_145615_create_books_published_table_disabled.php index c4e00ea..4141e06 100644 --- a/database/migrations/2025_03_31_145615_create_books_published_table.php +++ b/database/migrations/2025_03_31_145615_create_books_published_table_disabled.php @@ -34,8 +34,8 @@ return new class extends Migration $table->timestamps(); }); // Add ISSN format check constraint manually - DB::statement("ALTER TABLE books_published ADD CONSTRAINT chk_issn_format CHECK (issn REGEXP '^[0-9]{4}-[0-9]{3}[0-9X]$')"); - + + } /** diff --git a/database/migrations/2025_10_17_161236_add_scholar_url_to_users_table.php b/database/migrations/2025_10_17_161236_add_scholar_url_to_users_table.php new file mode 100644 index 0000000..6e91200 --- /dev/null +++ b/database/migrations/2025_10_17_161236_add_scholar_url_to_users_table.php @@ -0,0 +1,22 @@ +string('scholar_url')->nullable()->after('scopus_id'); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('scholar_url'); + }); + } +}; diff --git a/database/migrations/2025_10_17_195531_make_publication_fields_nullable.php b/database/migrations/2025_10_17_195531_make_publication_fields_nullable.php new file mode 100644 index 0000000..3d89e33 --- /dev/null +++ b/database/migrations/2025_10_17_195531_make_publication_fields_nullable.php @@ -0,0 +1,26 @@ +string('organizing_institute')->nullable()->change(); + $table->string('venue_address')->nullable()->change(); + $table->string('affiliation')->nullable()->change(); + }); + } + + public function down(): void + { + Schema::table('publications', function (Blueprint $table) { + $table->string('organizing_institute')->nullable(false)->change(); + $table->string('venue_address')->nullable(false)->change(); + $table->string('affiliation')->nullable(false)->change(); + }); + } +}; diff --git a/database/migrations/create_faculty_profiles_table.php b/database/migrations/create_faculty_profiles_table.php new file mode 100644 index 0000000..8fba6a4 --- /dev/null +++ b/database/migrations/create_faculty_profiles_table.php @@ -0,0 +1,45 @@ +id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + + $table->string('name'); + $table->string('email')->unique(); + $table->string('phone_number')->nullable(); + $table->string('vidhvan_id'); + $table->string('google_scholar_id'); + $table->string('linkedin_id')->nullable(); + $table->string('designation')->nullable(); + $table->string('salutation'); + $table->json('qualification')->nullable(); // multiple quals + $table->text('address')->nullable(); + $table->string('employee_id'); + $table->string('department_name'); + $table->string('programme_name'); + $table->date('date_of_joining'); + $table->string('profile_photo_path')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('faculty_profiles'); + } +}; diff --git a/resources/views/faculty/profile.blade.php b/resources/views/faculty/profile.blade.php new file mode 100644 index 0000000..4f34361 --- /dev/null +++ b/resources/views/faculty/profile.blade.php @@ -0,0 +1,14 @@ +@extends('layouts.app') + +@section('content') +
+

Faculty Profile

+ +
+

Name: {{ $profile->name }}

+

Email: {{ $profile->email }}

+

Phone Number: {{ $profile->phone_number ?: 'N/A' }}

+

Department: {{ $profile->department_name ?: 'N/A' }}

+
+
+@endsection diff --git a/resources/views/layouts/partials/navbar.blade.php b/resources/views/layouts/partials/navbar.blade.php index c15161e..0b9a938 100644 --- a/resources/views/layouts/partials/navbar.blade.php +++ b/resources/views/layouts/partials/navbar.blade.php @@ -1,4 +1,6 @@ + + \ No newline at end of file + diff --git a/resources/views/pages/publications/index.blade.php b/resources/views/pages/publications/index.blade.php index 7314803..8c8e5b6 100644 --- a/resources/views/pages/publications/index.blade.php +++ b/resources/views/pages/publications/index.blade.php @@ -1,6 +1,7 @@ @extends('layouts.app') @section('content') +
@@ -9,8 +10,54 @@

All Publications

+ @if(auth()->user()->role->name === 'Faculty') + + + @endif
+ + @if(auth()->user()->role->name === 'Faculty') + + @endif +
@@ -45,6 +92,9 @@ @section('scripts') -@endsection \ No newline at end of file +@endsection diff --git a/resources/views/profile/edit.blade.php b/resources/views/profile/edit.blade.php index e0e1d38..48addf1 100644 --- a/resources/views/profile/edit.blade.php +++ b/resources/views/profile/edit.blade.php @@ -1,29 +1,105 @@ - - -

- {{ __('Profile') }} -

-
+@extends('layouts.app') -
-
-
-
- @include('profile.partials.update-profile-information-form') -
+@section('content') +
+

Edit Faculty Profile

+ +
+ @csrf + @method('PUT') + +
+
+ +
-
-
- @include('profile.partials.update-password-form') -
+
+ +
-
-
- @include('profile.partials.delete-user-form') -
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + Separate multiple qualifications with commas (e.g., B.Tech, M.Tech, Ph.D) +
+ +
+ + +
+ +
+ + + + @if ($profile->profile_photo_path) +
+ Profile Photo +
+ @endif
-
- + +
+ +
+ +
+@endsection diff --git a/resources/views/profile/index.blade.php b/resources/views/profile/index.blade.php new file mode 100644 index 0000000..ef62e55 --- /dev/null +++ b/resources/views/profile/index.blade.php @@ -0,0 +1,89 @@ +@extends('layouts.app') + +@section('content') +
+ @if(session('success')) + + @endif + +

Faculty Profile

+ +
+
+
+ @if($profile->profile_photo_path) + Profile Photo + @else + Default Photo + @endif +
+
+
{{ $profile->salutation }} {{ $profile->name }}
+

{{ $profile->designation }}

+

{{ $profile->department_name }} — {{ $profile->programme_name }}

+ + {{-- Compact links row --}} +
+ @if($profile->google_scholar_id) + + Scholar + + @endif + + @if($profile->vidhwan_id) + + Vidhwan + + @endif + + @if($profile->linkedin_id) + + LinkedIn + + @endif +
+
+
+ +
+ +
+
+

Email: {{ $profile->email }}

+

Phone: {{ $profile->phone_number ?: 'N/A' }}

+

Employee ID: {{ $profile->employee_id ?: 'N/A' }}

+

Date of Joining: + {{ $profile->date_of_joining ? $profile->date_of_joining->format('d M Y') : 'N/A' }} +

+
+ +
+

Qualification: + {{ is_array($profile->qualification) ? implode(', ', $profile->qualification) : ($profile->qualification ?: 'N/A') }} +

+

Address: {{ $profile->address ?: 'N/A' }}

+

Vidhwan ID: {{ $profile->vidhwan_id ?: 'N/A' }}

+
+
+ + +
+
+@endsection diff --git a/routes/web.php b/routes/web.php index f9e326c..7fc3d8d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,8 +2,6 @@ use App\Http\Controllers\ActivitiesAttendedController; use App\Http\Controllers\ActivitiesOrganisedController; -use App\Http\Controllers\ProfileController; -use Illuminate\Support\Facades\Route; use App\Http\Controllers\RoleController; use App\Http\Controllers\DepartmentController; use App\Http\Controllers\UserController; @@ -18,6 +16,7 @@ use App\Http\Controllers\OnlineCoursesController; use App\Http\Controllers\PatentsController; use App\Http\Controllers\PublicationsController; use App\Http\Middleware\CheckRole; +use App\Http\Controllers\FacultyProfileController; Route::get('/', function () { return redirect('/login'); @@ -39,12 +38,6 @@ Route::get('/dashboard', function () { })->middleware(['auth', 'verified'])->name('dashboard'); -Route::middleware('auth')->group(function () { - Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); - Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); - Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); -}); - // Activities Attended common routes Route::delete('/activities-attended/{id}', [ActivitiesAttendedController::class, 'destroy'])->name('activitiesAttended.destroy'); @@ -199,6 +192,11 @@ Route::middleware(['auth', CheckRole::class . ':Coordinator'])->group(function ( Route::middleware(['auth', CheckRole::class . ':Faculty'])->group(function () { Route::get('/faculty', [FacultyController::class, 'index'])->name('faculty.dashboard'); + // Faculty Profile Routes - KEPT AS REQUESTED + Route::get('/faculty/profile', [FacultyProfileController::class, 'index'])->name('faculty.profile.index'); + Route::get('/faculty/profile/edit', [FacultyProfileController::class, 'edit'])->name('faculty.profile.edit'); + Route::put('/faculty/profile', [FacultyProfileController::class, 'update'])->name('faculty.profile.update'); + // Activities Attended Routes Route::get('/faculty/ActivitiesAttendedForm', [FacultyController::class, 'ActivitiesAttendedForm'])->name('faculty.ActivitiesAttendedForm'); Route::post('/faculty/ActivitiesAttendedFormResponse', [FacultyController::class, 'ActivitiesAttendedFormResponse'])->name('faculty.ActivitiesAttendedFormResponse'); @@ -262,6 +260,16 @@ Route::middleware(['auth', CheckRole::class . ':Faculty'])->group(function () { Route::get('/faculty/PatentsResponses/data', [PatentsController::class, 'getPatentsResponses'])->name('faculty.PatentsResponses.data'); Route::get('/faculty/Patents/{id}/edit', [PatentsController::class, 'edit'])->name('faculty.Patents.edit'); Route::put('/faculty/Patents/{id}', [PatentsController::class, 'update'])->name('faculty.Patents.update'); + + // Google Scholar import routes (FIXED: changed fetchFromScholar to fetchScholar) + Route::post('/faculty/publications/fetch-scholar', [PublicationsController::class, 'fetchScholar']) + ->name('faculty.publications.fetchScholar'); + + Route::post('/faculty/publications/import-scholar', [PublicationsController::class, 'importScholar']) + ->name('faculty.publications.importScholar'); + + Route::post('/faculty/publications/import-selected', [PublicationsController::class, 'importSelected']) + ->name('faculty.publications.importSelected'); }); Route::post('/missing-proofs/check', [MissingProofsController::class, 'checkAndSendEmails'])->name('missing-proofs.check'); @@ -286,7 +294,7 @@ Route::prefix('admin')->name('admin.')->group(function () { Route::get('analytics/externalEngagement', [AdminController::class, 'analyticsExternalEngagement'])->name('analytics.externalEngagement'); Route::get('analytics/onlineCourse', [AdminController::class, 'analyticsOnlineCourse'])->name('analytics.onlineCourse'); Route::get('analytics/patent', [AdminController::class, 'analyticsPatent'])->name('analytics.patent'); - + Route::get('analytics/comparison', [AdminController::class, 'analyticsComparison'])->name('analytics.comparison'); Route::get('analytics/contribution', [AdminController::class, 'analyticsContribution'])->name('analytics.contribution'); }); @@ -296,3 +304,8 @@ Route::post('/admin/download-proofs', [AdminController::class, 'downloadProofs'] // Google Login Route Route::get('/auth/google', [App\Http\Controllers\Auth\GoogleController::class, 'redirectToGoogle'])->name('google.login'); Route::get('/auth/google/callback', [App\Http\Controllers\Auth\GoogleController::class, 'handleGoogleCallback'])->name('google.callback'); + +// ✅ Fallback route alias to avoid 'profile.edit' missing error +Route::get('/profile', function () { + return redirect()->route('faculty.profile.edit'); +})->name('profile.edit');