diff --git a/app/Http/Controllers/Auth/GoogleController.php b/app/Http/Controllers/Auth/GoogleController.php index 3f1ca6f..40c03d8 100644 --- a/app/Http/Controllers/Auth/GoogleController.php +++ b/app/Http/Controllers/Auth/GoogleController.php @@ -15,7 +15,9 @@ class GoogleController extends Controller */ public function redirectToGoogle() { - return Socialite::driver('google')->redirect(); + // Prefer to hint Google to show accounts from the somaiya.edu domain. + // This is only a UI hint — always validate the domain on the callback. + return Socialite::driver('google')->with(['hd' => 'somaiya.edu'])->redirect(); } /** @@ -26,10 +28,20 @@ class GoogleController extends Controller try { $googleUser = Socialite::driver('google')->user(); + // Validate that the user belongs to the somaiya.edu domain. + // Google may return a 'hd' (hosted domain) claim; fall back to parsing the email. + $email = $googleUser->getEmail(); + $hostedDomain = data_get($googleUser->user, 'hd'); + $domain = $hostedDomain ?: (strpos($email, '@') !== false ? substr(strrchr($email, "@"), 1) : null); + + if ($domain !== 'somaiya.edu') { + return redirect()->route('login')->withErrors(['error' => 'Please sign in using a somaiya.edu account.']); + } + $user = User::firstOrCreate( - ['email' => $googleUser->getEmail()], + ['email' => $email], [ - 'name' => $googleUser->getName(), + 'name' => $googleUser->getName() ?: $email, 'password' => bcrypt(Str::random(16)), // Generate a random password ] ); diff --git a/app/Http/Controllers/CoordinatorController.php b/app/Http/Controllers/CoordinatorController.php index aa1ee01..afcb45b 100644 --- a/app/Http/Controllers/CoordinatorController.php +++ b/app/Http/Controllers/CoordinatorController.php @@ -2,57 +2,392 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; -use Yajra\DataTables\Facades\DataTables; -use Illuminate\Support\Facades\Auth; use App\Models\ActivitiesAttended; +use App\Models\ActivitiesOrganised; +use App\Models\BooksPublished; use App\Models\Department; +use App\Models\ExternalEngagement; +use App\Models\IvOrganised; +use App\Models\OnlineCourse; +use App\Models\Patent; +use App\Models\Publication; +use App\Services\ProofDownloadService; +use Illuminate\Http\Request; +// use Yajra\DataTables\Facades\DataTables; +use Illuminate\Support\Facades\Auth; class CoordinatorController extends Controller { // Coordinator dashboard (optional) public function index() { - return view('coordinator.dashboard'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + + // Collect distinct years from all relevant date fields + $years = collect([ + ActivitiesAttended::where('department_id', $user->department_id) ->selectRaw('YEAR(end_date) as year')->distinct()->pluck('year'), + + ActivitiesOrganised::where('department_id', $user->department_id) ->selectRaw('YEAR(end_date) as year')->distinct()->pluck('year'), + + BooksPublished::where('department_id', $user->department_id) ->selectRaw('YEAR(date_of_publication) as year')->distinct()->pluck('year'), + + ExternalEngagement::where('department_id', $user->department_id) ->selectRaw('YEAR(end_date) as year')->distinct()->pluck('year'), + + IvOrganised::where('department_id', $user->department_id) ->selectRaw('YEAR(end_date) as year')->distinct()->pluck('year'), + + OnlineCourse::where('department_id', $user->department_id) ->selectRaw('YEAR(end_date) as year')->distinct()->pluck('year'), + + Patent::where('department_id', $user->department_id) ->selectRaw('YEAR(date_of_submission) as year')->distinct()->pluck('year'), + + Publication::where('department_id', $user->department_id) ->selectRaw('YEAR(end_date) as year')->distinct()->pluck('year'), + + ])->flatten()->filter()->unique()->sortDesc()->values(); + + return view('coordinator.dashboard', compact('departments', 'years')); } // View responses submitted by users public function viewActivitiesAttendedResponses() { - $departments = Department::all(); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); return view('pages.activities-attended.index', compact('departments')); } public function viewActivitiesOrganisedResponses() { - return view('pages.activities-organised.index'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + return view('pages.activities-organised.index', compact('departments')); } public function viewIvOrganisedResponses() { - return view('pages.iv-organised.index'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + return view('pages.iv-organised.index', compact('departments')); } public function viewPublicationsResponses() { - return view('pages.publications.index'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + return view('pages.publications.index', compact('departments')); } public function viewBooksPublishedResponses() { - return view('pages.booksPublished.index'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + return view('pages.booksPublished.index', compact('departments')); } public function viewExternalEngagementResponses() { - return view('pages.externalEngagement.index'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + return view('pages.externalEngagement.index', compact('departments')); } + public function viewOnlineCoursesResponses() { - return view('pages.onlineCourses.index'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + return view('pages.onlineCourses.index', compact('departments')); } + public function viewPatentsResponses() { - return view('pages.patents.index'); + $user = Auth::user(); + $departments = Department::where('id', $user->department_id)->get(); + return view('pages.patents.index', compact('departments')); + } + + public function downloadProofs(Request $request, ProofDownloadService $proofDownloadService) + { + // Validate the request + $request->validate([ + 'ids' => 'required|string', + 'model' => 'sometimes|string', + ]); + + $ids = json_decode($request->input('ids')); + $modelName = $request->input('model', 'ActivitiesAttended'); + + // Model mapping + $modelMap = [ + 'ActivitiesAttended' => ActivitiesAttended::class, + 'ActivitiesOrganised' => ActivitiesOrganised::class, + 'IvOrganised' => IvOrganised::class, + 'Publication' => Publication::class, + 'BooksPublished' => BooksPublished::class, + 'ExternalEngagement' => ExternalEngagement::class, + 'OnlineCourse' => OnlineCourse::class, + 'Patent' => Patent::class, + ]; + + // Get the model class from the map or default to ActivitiesAttended + $modelClass = $modelMap[$modelName] ?? ActivitiesAttended::class; + + $result = $proofDownloadService->downloadProofs($modelClass, $ids); + + if (isset($result['error'])) { + return back()->with('error', $result['error']); + } + + // Return the zip file as a download + return response()->download($result['filePath'], $result['fileName'])->deleteFileAfterSend(true); + } + + public function analyticsActivitiesAttended() + { + $data = ActivitiesAttended::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'activities_attendeds.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsActivitiesOrganised() + { + $data = ActivitiesOrganised::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'activities_organiseds.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsIvOrganised() + { + $data = IvOrganised::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'iv_organiseds.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsPaperPublished() + { + $data = Publication::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'publications.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsBooksPublished() + { + $data = BooksPublished::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'books_published.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsExternalEngagement() + { + $data = ExternalEngagement::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'external_engagements.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsOnlineCourse() + { + $data = OnlineCourse::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'online_courses.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsPatent() + { + $data = Patent::selectRaw('departments.name as department, COUNT(*) as count') + ->join('departments', 'patents.department_id', '=', 'departments.id') + ->groupBy('departments.name') + ->get(); + + $total = $data->sum('count'); + + return response()->json([ + 'labels' => $data->pluck('department'), + 'values' => $data->pluck('count'), + 'total' => $total, + ]); + } + + public function analyticsComparison(Request $request) + { + $user = Auth::user(); + $departmentId = $request->query('department_id'); + if (empty($departmentId) && $user && $user->role_id == 2) { + $departmentId = $user->department_id; + } + $year = $request->query('year'); + + $models = [ + 'ActivitiesAttended' => ['class' => ActivitiesAttended::class, 'date_field' => 'end_date'], + 'ActivitiesOrganised' => ['class' => ActivitiesOrganised::class, 'date_field' => 'end_date'], + 'BooksPublished' => ['class' => BooksPublished::class, 'date_field' => 'date_of_publication'], + 'ExternalEngagement' => ['class' => ExternalEngagement::class, 'date_field' => 'end_date'], + 'IvOrganised' => ['class' => IvOrganised::class, 'date_field' => 'end_date'], + 'OnlineCourse' => ['class' => OnlineCourse::class, 'date_field' => 'end_date'], + 'Patent' => ['class' => Patent::class, 'date_field' => 'date_of_submission'], + 'Publication' => ['class' => Publication::class, 'date_field' => 'end_date'], + ]; + + $data = []; + + foreach ($models as $label => $modelConfig) { + $query = $modelConfig['class']::query(); + if($departmentId) { + $query->where('department_id', $departmentId); + } + if ($year && $year !== 'all') { + $query->whereYear($modelConfig['date_field'], $year); + } + $count = $query->count(); + if ($count > 0) { // Only include data with non-zero count + $data[] = [ + 'label' => $label, + 'count' => $count, + ]; + } + } + + $total = array_sum(array_column($data, 'count')); + + return response()->json([ + 'labels' => array_column($data, 'label'), + 'values' => array_column($data, 'count'), + 'total' => $total + ]); + } + + public function analyticsContribution(Request $request) + { + $user = Auth::user(); + $model = $request->query('model', 'all'); + $year = $request->query('year'); + $departmentId = $request->query('department_id'); + if (empty($departmentId) && $user && $user->role_id == 2) { + $departmentId = $user->department_id; + } + + $models = [ + 'ActivitiesAttended' => ['class' => ActivitiesAttended::class, 'date_field' => 'end_date'], + 'ActivitiesOrganised' => ['class' => ActivitiesOrganised::class, 'date_field' => 'end_date'], + 'BooksPublished' => ['class' => BooksPublished::class, 'date_field' => 'date_of_publication'], + 'ExternalEngagement' => ['class' => ExternalEngagement::class, 'date_field' => 'end_date'], + 'IvOrganised' => ['class' => IvOrganised::class, 'date_field' => 'end_date'], + 'OnlineCourse' => ['class' => OnlineCourse::class, 'date_field' => 'end_date'], + 'Patent' => ['class' => Patent::class, 'date_field' => 'date_of_submission'], + 'Publication' => ['class' => Publication::class, 'date_field' => 'end_date'], + ]; + + $data = []; + + if ($model === 'all') { + $departments = Department::when($departmentId, function ($query) use ($departmentId) { + return $query->where('id', $departmentId); + })->get(); + + foreach ($departments as $department) { + $count = 0; + foreach ($models as $modelConfig) { + $query = $modelConfig['class']::where('department_id', $department->id); + if ($year && $year !== 'all') { + $query->whereYear($modelConfig['date_field'], $year); + } + $count += $query->count(); + } + if ($count > 0) { // Only include data with non-zero count + $data[] = [ + 'label' => $department->name, + 'count' => $count, + ]; + } + } + } else { + $modelConfig = $models[$model]; + $modelClass = $modelConfig['class']; + $table = (new $modelClass)->getTable(); + + $query = $modelClass::selectRaw('departments.name as label, COUNT(*) as count') + ->join('departments', $table . '.department_id', '=', 'departments.id') + ->groupBy('departments.name'); + + if ($year && $year !== 'all') { + $query->whereYear($table . '.' . $modelConfig['date_field'], $year); + } + + if ($departmentId) { + $query->where($table . '.department_id', $departmentId); + } + + $data = $query->get()->filter(function ($item) { + return $item['count'] > 0; // Filter out data with zero count + })->toArray(); + } + + $total = array_sum(array_column($data, 'count')); + + return response()->json([ + 'labels' => array_column($data, 'label'), + 'values' => array_column($data, 'count'), + 'total' => $total + ]); } } diff --git a/app/Http/Controllers/PublicationsController.php b/app/Http/Controllers/PublicationsController.php index b43d7ee..fe03206 100644 --- a/app/Http/Controllers/PublicationsController.php +++ b/app/Http/Controllers/PublicationsController.php @@ -201,25 +201,39 @@ class PublicationsController extends Controller if (!$user || !$user->scholar_url) { return response()->json([ - 'success' => false, - 'message' => 'No Google Scholar URL found for the logged-in user.' + 'error' => 'No Google Scholar URL found for the logged-in user.' ], 400); } $scholarService = new \App\Services\ScholarService(); - $publications = $scholarService->fetchPublications($user->scholar_url); + $result = $scholarService->fetchPublications($user->scholar_url); + + // If the service returned an error payload, forward it to UI + if (is_array($result) && array_key_exists('error', $result)) { + // Log more details if available + if (isset($result['body'])) { + \Log::error('ScholarService error body', ['body' => $result['body']]); + } elseif (isset($result['raw'])) { + \Log::warning('ScholarService returned empty articles', ['raw' => $result['raw']]); + } + + return response()->json([ + 'error' => $result['error'] + ], 200); + } + + // Expecting successful shape ['data' => [...]] + $publications = $result['data'] ?? []; if (empty($publications)) { return response()->json([ - 'success' => false, - 'message' => 'No publications found from the provided Scholar profile.' - ]); + 'error' => 'No publications found from the provided Scholar profile.' + ], 200); } return response()->json([ - 'success' => true, - 'count' => count($publications), 'data' => $publications, + 'count' => count($publications), ]); } catch (\Throwable $e) { diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index d94ba4c..23b6a77 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -45,7 +45,7 @@ class UserController extends Controller ->addColumn('role_name', function ($response) { $roles = [ 1 => 'Admin', - 2 => 'Coordinator', + 2 => 'HOD', 3 => 'Faculty', ]; $options = ''; diff --git a/app/Services/ScholarService.php b/app/Services/ScholarService.php index eee285e..b54da14 100644 --- a/app/Services/ScholarService.php +++ b/app/Services/ScholarService.php @@ -10,20 +10,22 @@ class ScholarService { try { // Extract Google Scholar profile ID (e.g., ssU-uBEAAAAJ) - preg_match('/user=([^&]+)/', $url, $match); + preg_match('/[?&]user=([^&]+)/', $url, $match); $profileId = $match[1] ?? null; if (!$profileId) { - \Log::warning('No Google Scholar ID found in URL: ' . $url); - return []; + $msg = 'No Google Scholar ID found in URL: ' . $url; + \Log::warning($msg); + return ['error' => $msg]; } // Load API key from .env $apiKey = env('SERPAPI_KEY'); if (!$apiKey) { - \Log::error('SERPAPI_KEY not found in environment variables'); - return []; + $msg = 'SERPAPI_KEY not found in environment variables'; + \Log::error($msg); + return ['error' => $msg]; } $endpoint = 'https://serpapi.com/search.json'; @@ -34,8 +36,10 @@ class ScholarService ]); if (!$response->successful()) { - \Log::error('Scholar API request failed: ' . $response->status()); - return []; + $status = $response->status(); + $body = $response->body(); + \Log::error('Scholar API request failed', ['status' => $status, 'body' => $body]); + return ['error' => 'Scholar API request failed: ' . $status, 'body' => $body]; } $json = $response->json(); @@ -46,8 +50,13 @@ class ScholarService ?? $json['results'] ?? []; + if (empty($articles)) { + \Log::warning('Scholar API returned no articles', ['profileId' => $profileId, 'response' => $json]); + return ['error' => 'No publications returned by Scholar API', 'raw' => $json]; + } + // ✅ Format and return - return collect($articles) + $formatted = collect($articles) ->map(function ($a) { return [ 'title' => $a['title'] ?? $a['name'] ?? null, @@ -59,6 +68,8 @@ class ScholarService }) ->toArray(); + return ['data' => $formatted]; + } catch (\Throwable $e) { \Log::error('ScholarService API error: ' . $e->getMessage()); return []; diff --git a/composer.json b/composer.json index d0d3ec4..eed2364 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "laravel/sail": "^1.26", "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.1", - "phpunit/phpunit": "^11.0.1" + "phpunit/phpunit": "^11.0.1", + "symfony/panther": "^2.2" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 76f4a4d..06f7694 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d9ba03cac29ec7ec3e39de5902752aa9", + "content-hash": "907f80ba2043dbb55bff6a61836bb507", "packages": [ { "name": "brick/math", @@ -7755,6 +7755,73 @@ }, "time": "2025-01-13T16:57:11+00:00" }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "time": "2025-07-25T09:04:22+00:00" + }, { "name": "mockery/mockery", "version": "1.6.12", @@ -8114,6 +8181,72 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "php-webdriver/webdriver", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "998e499b786805568deaf8cbf06f4044f05d91bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/998e499b786805568deaf8cbf06f4044f05d91bf", + "reference": "998e499b786805568deaf8cbf06f4044f05d91bf", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^7.3 || ^8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^5.0 || ^6.0 || ^7.0" + }, + "replace": { + "facebook/webdriver": "*" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.20.0", + "ondram/ci-detector": "^4.0", + "php-coveralls/php-coveralls": "^2.4", + "php-mock/php-mock-phpunit": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Exception/TimeoutException.php" + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "support": { + "issues": "https://github.com/php-webdriver/php-webdriver/issues", + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.15.2" + }, + "time": "2024-11-21T15:12:59+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "11.0.8", @@ -9516,6 +9649,581 @@ ], "time": "2024-10-20T05:08:20+00:00" }, + { + "name": "symfony/browser-kit", + "version": "v7.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "e9a9fd604296b17bf90939c3647069f1f16ef04e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e9a9fd604296b17bf90939c3647069f1f16ef04e", + "reference": "e9a9fd604296b17bf90939c3647069f1f16ef04e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/dom-crawler": "^6.4|^7.0" + }, + "require-dev": { + "symfony/css-selector": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v7.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-11-05T07:57:47+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v7.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "98af8bb46c56aedd9dd5a7f0414fc72bf2dcfe69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/98af8bb46c56aedd9dd5a7f0414fc72bf2dcfe69", + "reference": "98af8bb46c56aedd9dd5a7f0414fc72bf2dcfe69", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.5", + "symfony/var-exporter": "^6.4.20|^7.2.5" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<6.4", + "symfony/finder": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "symfony/config": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v7.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-10-31T10:11:11+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/efa076ea0eeff504383ff0dcf827ea5ce15690ba", + "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba", + "shasum": "" + }, + "require": { + "masterminds/html5": "^2.6", + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-06T20:13:54+00:00" + }, + { + "name": "symfony/http-client", + "version": "v7.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de", + "reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "amphp/amp": "<2.5", + "amphp/socket": "<1.1", + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/amphp-http-client-meta": "^1.0|^2.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v7.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-11-05T17:41:46+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "75d7043853a42837e68111812f4d964b01e5101c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c", + "reference": "75d7043853a42837e68111812f4d964b01e5101c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-29T11:18:49+00:00" + }, + { + "name": "symfony/panther", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/panther.git", + "reference": "b7e0f834c9046918972edb3dde2ecc4a20f6155e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/panther/zipball/b7e0f834c9046918972edb3dde2ecc4a20f6155e", + "reference": "b7e0f834c9046918972edb3dde2ecc4a20f6155e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=8.0", + "php-webdriver/webdriver": "^1.8.2", + "symfony/browser-kit": "^5.4 || ^6.4 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", + "symfony/deprecation-contracts": "^2.4 || ^3", + "symfony/dom-crawler": "^5.4 || ^6.4 || ^7.0", + "symfony/http-client": "^6.4 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.4 || ^7.0", + "symfony/process": "^5.4 || ^6.4 || ^7.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4 || ^6.4 || ^7.0", + "symfony/framework-bundle": "^5.4 || ^6.4 || ^7.0", + "symfony/mime": "^5.4 || ^6.4 || ^7.0", + "symfony/phpunit-bridge": "^7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Panther\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com", + "homepage": "https://dunglas.fr" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A browser testing and web scraping library for PHP and Symfony.", + "homepage": "https://dunglas.fr", + "keywords": [ + "e2e", + "scraping", + "selenium", + "symfony", + "testing", + "webdriver" + ], + "support": { + "issues": "https://github.com/symfony/panther/issues", + "source": "https://github.com/symfony/panther/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://www.panthera.org/donate", + "type": "custom" + }, + { + "url": "https://github.com/dunglas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/panther", + "type": "tidelift" + } + ], + "time": "2025-01-30T13:11:55+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v7.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-09-11T10:12:26+00:00" + }, { "name": "symfony/yaml", "version": "v7.2.0", @@ -9641,12 +10349,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/resources/views/coordinator/dashboard.blade.php b/resources/views/coordinator/dashboard.blade.php index 6f68b7c..1c92adc 100644 --- a/resources/views/coordinator/dashboard.blade.php +++ b/resources/views/coordinator/dashboard.blade.php @@ -1,22 +1,223 @@ @extends('layouts.app') @section('header') -