feat(profile,publications): integrate working Google Scholar import flow and unified faculty profile system

This commit is contained in:
Harshil Vora
2025-10-18 02:53:32 -07:00
parent 8fe0ef8112
commit ba28588e38
19 changed files with 915 additions and 139 deletions

View File

@@ -1,6 +1,7 @@
@extends('layouts.app')
@section('content')
<meta name="csrf-token" content="{{ csrf_token() }}">
<div class="container py-4">
<div class="row">
<div class="col-12">
@@ -9,8 +10,54 @@
<h3 class="page-title m-0">
<i class="fas fa-book-open me-2 text-primary"></i>All Publications
</h3>
@if(auth()->user()->role->name === 'Faculty')
<input type="hidden" id="scholar-url" value="{{ $scholarUrl ?? '' }}">
<button id="btn-fetch-scholar" class="btn btn-outline-primary">
<i class="fab fa-google me-1"></i> Import from Google Scholar
</button>
@endif
</div>
<div class="card-body">
<!-- 🔹 Google Scholar Modal -->
@if(auth()->user()->role->name === 'Faculty')
<div class="modal fade" id="scholarModal" tabindex="-1" aria-labelledby="scholarModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="scholarModalLabel">Select Publications to Import</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="scholar-loading" class="text-center my-4 d-none">
<div class="spinner-border text-primary"></div>
<p class="mt-2">Fetching from Google Scholar...</p>
</div>
<div id="scholar-table-wrapper" class="table-responsive d-none">
<table class="table table-bordered align-middle">
<thead>
<tr>
<th><input type="checkbox" id="select-all"></th>
<th>Title</th>
<th>Authors</th>
<th>Journal</th>
<th>Year</th>
<th>Link</th>
</tr>
</thead>
<tbody id="scholar-table-body"></tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button id="btn-import-selected" class="btn btn-success" disabled>Import Selected</button>
</div>
</div>
</div>
</div>
@endif
<!-- Table -->
<div class="table-responsive">
<table id="publications-table" class="table table-striped table-hover">
@@ -45,6 +92,9 @@
@section('scripts')
<script>
// 🔹 Global CSRF setup for all fetch() calls
window.csrfToken = document.querySelector('meta[name="csrf-token"]').content;
$(document).ready(function() {
const sheetName = "Publications";
@@ -111,5 +161,129 @@
initAjaxRoute(dataRoute);
});
// 🔹 Handle delete button click (for AJAX DataTable)
$(document).on('click', '.delete-btn', function () {
const button = $(this);
const id = button.data('id');
const url = button.data('url');
if (!confirm('Are you sure you want to delete this publication?')) return;
fetch(url, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': window.csrfToken,
'Accept': 'application/json',
},
})
.then(res => res.json())
.then(data => {
alert(data.success || 'Deleted successfully');
$('#publications-table').DataTable().ajax.reload(null, false);
})
.catch(err => {
console.error(err);
alert('Failed to delete publication.');
});
});
// 🔹 Google Scholar Integration Script (Faculty Only)
@if(auth()->user()->role->name === 'Faculty')
const fetchBtn = document.getElementById('btn-fetch-scholar');
if (fetchBtn) {
const modal = new bootstrap.Modal(document.getElementById('scholarModal'));
const body = document.getElementById('scholar-table-body');
const loading = document.getElementById('scholar-loading');
const tableWrap = document.getElementById('scholar-table-wrapper');
const importBtn = document.getElementById('btn-import-selected');
const scholarUrlInput = document.getElementById('scholar-url');
fetchBtn.addEventListener('click', () => {
const scholarUrl = scholarUrlInput.value.trim();
if (!scholarUrl) {
alert('Please set your Google Scholar profile URL in your profile settings first.');
return;
}
modal.show();
loading.classList.remove('d-none');
tableWrap.classList.add('d-none');
body.innerHTML = '';
// ✅ No need to redeclare scholarUrl here again
fetch("{{ route('faculty.publications.fetchScholar') }}", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": window.csrfToken
},
body: JSON.stringify({ url: scholarUrl })
})
.then(res => res.json())
.then(data => {
loading.classList.add('d-none');
if (data.error) {
body.innerHTML = `<tr><td colspan='6' class='text-danger text-center'>${data.error}</td></tr>`;
} else {
const pubs = data.data;
pubs.forEach((p, i) => {
body.innerHTML += `
<tr>
<td><input type="checkbox" class="pub-check" data-index="${i}"></td>
<td>${p.title}</td>
<td>${p.authors || '-'}</td>
<td>${p.journal || '-'}</td>
<td>${p.year || '-'}</td>
<td><a href="${p.link}" target="_blank">View</a></td>
</tr>`;
});
tableWrap.classList.remove('d-none');
importBtn.disabled = false;
}
})
.catch(() => {
loading.classList.add('d-none');
body.innerHTML = `<tr><td colspan='6' class='text-danger text-center'>Failed to fetch data.</td></tr>`;
});
});
document.getElementById('select-all').addEventListener('change', function() {
document.querySelectorAll('.pub-check').forEach(cb => cb.checked = this.checked);
});
importBtn.addEventListener('click', () => {
const selected = Array.from(document.querySelectorAll('.pub-check:checked')).map(cb => {
const row = cb.closest('tr').children;
return {
title: row[1].innerText.trim(),
authors: row[2].innerText.trim(),
journal: row[3].innerText.trim(),
year: row[4].innerText.trim(),
link: row[5].querySelector('a')?.href || null
};
});
if (selected.length === 0) return alert("Please select at least one publication.");
fetch("{{ route('faculty.publications.importScholar') }}", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": window.csrfToken
},
body: JSON.stringify({ publications: selected })
})
.then(res => res.json())
.then(data => {
alert(data.message);
modal.hide();
location.reload();
})
.catch(() => alert("Import failed!"));
});
}
@endif
</script>
@endsection
@endsection