Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions class/Command/ShowReports.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
* This file is part of Internship Inventory.
*
* Internship Inventory is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.

* Internship Inventory is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 3
* along with Internship Inventory. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2011-2025 Appalachian State University
*/

namespace Intern\Command;

use \Intern\UI\NotifyUI;
use \Intern\ReportsView;

class ShowReports
{
public function __construct() {}

public function execute(): ReportsView
{
// Check permissions
if (!\Current_User::allow('intern', 'view_reports')) {
\NQ::simple('intern', NotifyUI::ERROR, 'You do not have permission to view reports.');
\NQ::close();
\PHPWS_Core::home();
}

$view = new ReportsView();

return $view;
}
}
5 changes: 5 additions & 0 deletions class/InternshipInventory.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,11 @@ public function handleRequest()
$ctrl = new Command\CipCodeRest();
$ctrl->execute();
break;
case 'reports':
$ctrl = new Command\ShowReports();
$view = $ctrl->execute();
$this->content = $view->render();
break;
default:
$menu = new UI\InternMenu();
$this->content = $menu->display();
Expand Down
37 changes: 37 additions & 0 deletions class/ReportsView.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/**
* This file is part of Internship Inventory.
*
* Internship Inventory is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.

* Internship Inventory is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 3
* along with Internship Inventory. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2011-2025 Appalachian State University
*/

namespace Intern;

class ReportsView
{
public function __construct() {}

public function render(): string
{
$tpl = array();

$tpl['vendor_bundle'] = AssetResolver::resolveJsPath('assets.json', 'vendor');
$tpl['entry_bundle'] = AssetResolver::resolveJsPath('assets.json', 'reports');

return \PHPWS_Template::process($tpl, 'intern', 'reports.tpl');
}
}
53 changes: 27 additions & 26 deletions class/UI/TopUI.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/**
* This file is part of Internship Inventory.
*
Expand Down Expand Up @@ -29,8 +30,7 @@
*/
class TopUI implements UI
{
public function display(){
}
public function display() {}

public static function plug()
{
Expand All @@ -48,9 +48,9 @@ public static function plug()
$auth = \Current_User::getAuthorization();

// If the user is a local user, show the dropdown with link to "my page" so they can change their password
if($auth->user->auth_script == 'local.php'){
if ($auth->user->auth_script == 'local.php') {
$tpl['PASSWORD_DROPDOWN_NAME'] = \Current_User::getDisplayName();;
}else {
} else {
// Otherwise, it's just their name with no dropdown menu
$tpl['USER_FULL_NAME'] = \Current_User::getDisplayName();
}
Expand All @@ -61,59 +61,60 @@ public static function plug()
$adminOptions = array();

// Edit terms
if(\Current_User::allow('intern', 'edit_terms')){
$adminOptions['EDIT_TERMS_LINK'] = \PHPWS_Text::secureLink('Terms','intern',array('action' => 'edit_terms'), null, null, 'dropdown-item');
if (\Current_User::allow('intern', 'edit_terms')) {
$adminOptions['EDIT_TERMS_LINK'] = \PHPWS_Text::secureLink('Terms', 'intern', array('action' => 'edit_terms'), null, null, 'dropdown-item');
}

// Edit departments
if(\Current_User::allow('intern', 'edit_dept')){
$adminOptions['EDIT_DEPARTMENTS_LINK'] = \PHPWS_Text::secureLink('Departments','intern',array('action' => 'showEditDept'), null, null, 'dropdown-item');
if (\Current_User::allow('intern', 'edit_dept')) {
$adminOptions['EDIT_DEPARTMENTS_LINK'] = \PHPWS_Text::secureLink('Departments', 'intern', array('action' => 'showEditDept'), null, null, 'dropdown-item');
}

// Edit list of majors
if(\Current_User::allow('intern', 'edit_major')){
$adminOptions['EDIT_MAJORS_LINK'] = \PHPWS_Text::secureLink('Majors &amp; Programs','intern',array('action' => 'showEditMajors'), null, null, 'dropdown-item');
if (\Current_User::allow('intern', 'edit_major')) {
$adminOptions['EDIT_MAJORS_LINK'] = \PHPWS_Text::secureLink('Majors &amp; Programs', 'intern', array('action' => 'showEditMajors'), null, null, 'dropdown-item');
}

// Edit list of student levels
if(\Current_User::allow('intern', 'edit_level')){
$adminOptions['EDIT_STUDENT_LEVEL'] = \PHPWS_Text::secureLink('Student Levels','intern',array('action' => 'edit_level'), null, null, 'dropdown-item');

if (\Current_User::allow('intern', 'edit_level')) {
$adminOptions['EDIT_STUDENT_LEVEL'] = \PHPWS_Text::secureLink('Student Levels', 'intern', array('action' => 'edit_level'), null, null, 'dropdown-item');
}

// Edit list of 'normal' courses
if(\Current_User::allow('intern', 'edit_courses')){
$adminOptions['EDIT_COURSES_LINK'] = \PHPWS_Text::secureLink('Course List','intern',array('action' => 'edit_courses'), null, null, 'dropdown-item');
if (\Current_User::allow('intern', 'edit_courses')) {
$adminOptions['EDIT_COURSES_LINK'] = \PHPWS_Text::secureLink('Course List', 'intern', array('action' => 'edit_courses'), null, null, 'dropdown-item');
}

// Edit list of states
if(\Current_User::allow('intern', 'edit_states')){
$adminOptions['EDIT_STATES_LINK'] = \PHPWS_Text::secureLink('Allowed States','intern',array('action' => 'edit_states'), null, null, 'dropdown-item');
if (\Current_User::allow('intern', 'edit_states')) {
$adminOptions['EDIT_STATES_LINK'] = \PHPWS_Text::secureLink('Allowed States', 'intern', array('action' => 'edit_states'), null, null, 'dropdown-item');
}


// Link to the Affiliation Agreements
if(\Current_User::allow('intern', 'affiliation_agreement')){
$adminOptions['AFFIL_AGREE_LINK'] = \PHPWS_Text::secureLink('Affiliation Agreements','intern',array('action' => 'showAffiliateAgreement'), null, null, 'dropdown-item');
if (\Current_User::allow('intern', 'affiliation_agreement')) {
$adminOptions['AFFIL_AGREE_LINK'] = \PHPWS_Text::secureLink('Affiliation Agreements', 'intern', array('action' => 'showAffiliateAgreement'), null, null, 'dropdown-item');
}

if(\Current_User::allow('intern', 'student_import')){
if (\Current_User::allow('intern', 'student_import')) {
$adminOptions['STUDENT_IMPORT'] = \PHPWS_Text::secureLink('Import Student Data', 'intern', array('action' => 'ShowStudentImport'), null, null, 'dropdown-item');
}

if(\Current_User::allow('intern', 'internship_import')){
if (\Current_User::allow('intern', 'internship_import')) {
$adminOptions['ACTIVITY_IMPORT'] = \PHPWS_Text::secureLink('Import Activities', 'intern', array('action' => 'ShowImportActivitiesStart'), null, null, 'dropdown-item');
}

if(\Current_User::isDeity()){
$adminOptions['CONTROL_PANEL'] = \PHPWS_Text::secureLink('Control Panel','controlpanel', null, null, null, 'dropdown-item');
$adminOptions['ADMIN_SETTINGS'] = \PHPWS_Text::secureLink('Admin Settings','intern',array('action' => 'showAdminSettings'), null, null, 'dropdown-item');
$adminOptions['EDIT_ADMINS_LINK'] = \PHPWS_Text::secureLink('Dept. Administrators','intern',array('action' => 'showEditAdmins'), null, null, 'dropdown-item');
if (\Current_User::isDeity()) {
$adminOptions['CONTROL_PANEL'] = \PHPWS_Text::secureLink('Control Panel', 'controlpanel', null, null, null, 'dropdown-item');
$adminOptions['ADMIN_SETTINGS'] = \PHPWS_Text::secureLink('Admin Settings', 'intern', array('action' => 'showAdminSettings'), null, null, 'dropdown-item');
$adminOptions['EDIT_ADMINS_LINK'] = \PHPWS_Text::secureLink('Dept. Administrators', 'intern', array('action' => 'showEditAdmins'), null, null, 'dropdown-item');
}

$adminOptions['REPORTS_LINK'] = \PHPWS_Text::secureLink('Reports', 'intern', array('action' => 'reports'), null, null, 'dropdown-item');

// If any admin options were added, them show the dropdown and merge those
// links into the main set of template tags
if(sizeof($adminOptions) > 0){
if (sizeof($adminOptions) > 0) {
$tpl['ADMIN_OPTIONS'] = ''; // dummy var to show dropdown menu in template
$tpl = array_merge($tpl, $adminOptions);
}
Expand Down
1 change: 1 addition & 0 deletions entryPoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports = {
majorSelector: JS_DIR + '/majorSelector/MajorSelector.jsx',
adminSettings: JS_DIR + '/settings/settings.jsx',
editTerms: JS_DIR + '/editTerms/TermEditor.jsx',
reports: JS_DIR + '/pages/ReportsPage.jsx',
vendor: ['jquery', 'react', 'react-dom', 'react-bootstrap']
}
};
66 changes: 66 additions & 0 deletions javascript/pages/ReportsPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';

import { ErrorBoundary } from 'react-error-boundary';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import Offcanvas from 'react-bootstrap/Offcanvas';
import ReportsTable from '../reports/ReportsTable';

const queryClient = new QueryClient();

function ReportsPage() {
const [showNewReportPanel, setShowNewReportPanel] = useState(false);

const handleShowNewReportPanel = () => {
setShowNewReportPanel(true);
};

const handleHideNewReportPanel = () => {
setShowNewReportPanel(false);
};

return (
<div>
<h1>
<i className="fa-regular fa-chart-bar"></i> Reports
</h1>

<div className="row mb-2">
<div className="col-md-12">
<div className="float-end">
<button type="button" className="btn btn-primary mb-3" onClick={handleShowNewReportPanel}>
<i className="fa-solid fa-file-circle-plus"></i> Create a Report
</button>
</div>
</div>
</div>

<Offcanvas show={showNewReportPanel} onHide={handleHideNewReportPanel} placement={'end'}>
<Offcanvas.Header closeButton>
<Offcanvas.Title>Create a New Report</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>Report creation functionality coming soon!</p>
</Offcanvas.Body>
</Offcanvas>

<ErrorBoundary fallback={<div>Something went wrong.</div>}>
<ReportsTable />
</ErrorBoundary>
</div>
);
}

const root = createRoot(document.getElementById('content'));

root.render(
<QueryClientProvider client={queryClient}>
<ReportsPage />
</QueryClientProvider>
);

if (process.env.NODE_ENV !== 'production') {
const axe = require('@axe-core/react');
axe(React, root, 1000);
}
35 changes: 35 additions & 0 deletions javascript/reports/ReportsTable.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { AgGridReact } from 'ag-grid-react';

import 'ag-grid-community/styles/ag-grid.css'; // Mandatory CSS required by the Data Grid
import 'ag-grid-community/styles/ag-theme-quartz.css'; // Optional Theme applied to the Data Grid

export default function ReportsTable({ isLoading }) {
if (isLoading) {
return (
<div className="spinner-border" role="status">
<span className="visually-hidden">Loading...</span>
</div>
);
}

const colDefs = [
{ field: 'reportName', width: 300, headerName: 'Report Name' },
{ field: 'createdBy', width: 200, headerName: 'Created By' },
{ field: 'createdOn', width: 180, headerName: 'Created On' },
{ field: 'download', width: 180, headerName: 'Download' }
];

const gridOptions = {
domLayout: 'autoHeight'
};

return (
<div>
<div className="ag-theme-quartz">
<AgGridReact columnDefs={colDefs} rowData={[]} gridOptions={gridOptions} />
</div>
</div>
);
}
4 changes: 4 additions & 0 deletions templates/reports.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div id="content"></div>

<script type="text/javascript" src="{vendor_bundle}"></script>
<script type="text/javascript" src="{entry_bundle}"></script>
4 changes: 4 additions & 0 deletions templates/top.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
<li>
<hr class="dropdown-divider">
</li>
<li>{REPORTS_LINK}</li>
<li>
<hr class="dropdown-divider">
</li>
<!-- BEGIN student_import -->
<li>{STUDENT_IMPORT}</li>
<!-- END student_import -->
Expand Down