Finished the first cut of adding asset assignments; Added a page to display asset assignments (need to allow removing them).

This commit is contained in:
2022-08-02 12:02:56 -07:00
parent bd88818428
commit 4560d7203d
7 changed files with 316 additions and 160 deletions

View File

@@ -23,12 +23,12 @@ const AssetAssignmentsSchema = new SimpleSchema({
label: "Assignee ID", label: "Assignee ID",
optional: false, optional: false,
}, },
assigneeType: { assigneeType: { // 0: Student, 1: Staff
type: SimpleSchema.Integer, type: SimpleSchema.Integer,
label: "Assignee Type", label: "Assignee Type",
optional: false, optional: false,
min: 1, min: 0,
max: 2, max: 1,
exclusiveMin: false, exclusiveMin: false,
exclusiveMax: false, exclusiveMax: false,
}, },
@@ -49,15 +49,26 @@ if (Meteor.isServer) {
}); });
} }
Meteor.methods({ Meteor.methods({
'assetAssignments.add'(assetId) { /**
// check(assetTypeId, String); * Assigns the asset to the assignee. The assignee should either be a Student or Staff member.
// check(assetId, String); * @param assetId The Mongo ID of the asset (asset._id).
* @param assigneeType One of: 'Student', 'Staff'
* @param assigneeId The Mongo ID of the Student or Staff (person._id).
*/
'AssetAssignments.add'(assetId, assigneeType, assigneeId) {
check(assigneeId, String);
check(assigneeType, String);
check(assetId, String);
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) { if(assigneeType !== 'Student' || assigneeType !== 'Staff') {
// Assets.insert({assetTypeId, assetId}); // Should never happen.
console.error("Error: Received incorrect assignee type in adding an assignment.");
}
else if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
AssetAssignments.insert({assetId, assigneeType: assigneeType === "Student" ? 0 : 1, assigneeId});
} }
}, },
'assetAssignments.remove'(_id) { 'AssetAssignments.remove'(_id) {
check(_id, String); check(_id, String);
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) { if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {

View File

@@ -14,10 +14,9 @@ if (Meteor.isServer) {
Meteor.publish('students', function(siteId) { Meteor.publish('students', function(siteId) {
return Students.find({siteId}); return Students.find({siteId});
}); });
}
Meteor.methods({ Meteor.methods({
async 'students.getPossibleGrades'() { 'students.getPossibleGrades'() {
return Students.rawCollection().distinct('grade', {}); return Students.rawCollection().distinct('grade', {});
}, },
/** /**
@@ -160,3 +159,5 @@ Meteor.methods({
} }
} }
}); });
}

View File

@@ -13,13 +13,6 @@
const assetTypesColumns = [ const assetTypesColumns = [
{ {
key: "_id",
title: "ID",
value: v => v._id,
minWidth: 20,
weight: 1,
cls: "id",
}, {
key: "name", key: "name",
title: "Name", title: "Name",
value: v => v.name, value: v => v.name,

View File

@@ -176,9 +176,9 @@
<h3>Aeries</h3> <h3>Aeries</h3>
<p>For the Aeries system, log into your Aeries web interface and navigate to the query page. Enter the following query:</p> <p>For the Aeries system, log into your Aeries web interface and navigate to the query page. Enter the following query:</p>
<h4>Pre-Rollover</h4> <h4>Pre-Rollover</h4>
<pre style="font-size: 0.7rem"><code>LIST STU STU.ID STU.SEM STU.FN STU.LN STU.NG BY STU.ID STU.NG STU.SEM IF STU.NG &gt;= 7 AND NG &lt;= 12 AND STU.NS = 5</code></pre> <pre style="font-size: 0.7rem"><code>LIST STU STU.ID STU.SEM STU.FN STU.LN STU.NG BY STU.ID STU.NG STU.SEM IF STU.NG &gt;= 7 AND STU.NG &lt;= 12 AND STU.NS = 5</code></pre>
<h4>Post-Rollover</h4> <h4>Post-Rollover</h4>
<pre style="font-size: 0.7rem"><code>LIST STU STU.ID STU.SEM STU.FN STU.LN STU.NG BY STU.ID STU.NG STU.SEM IF STU.NG &gt;= 7 AND GR &lt;= 12 AND STU.SC = 5</code></pre> <pre style="font-size: 0.7rem"><code>LIST STU STU.ID STU.SEM STU.FN STU.LN STU.GR BY STU.ID STU.GR STU.SEM IF STU.GR &gt;= 7 AND STU.GR &lt;= 12 AND STU.SC = 5</code></pre>
<p>Run the query and validate that all students have an email address and a student ID. You likely also want to check that the student `next grade (NG)` field is set correctly for the students (pre-rollover).</p> <p>Run the query and validate that all students have an email address and a student ID. You likely also want to check that the student `next grade (NG)` field is set correctly for the students (pre-rollover).</p>
<p>You have two options for export. You can:</p> <p>You have two options for export. You can:</p>
<ol class="help"> <ol class="help">

View File

@@ -7,6 +7,7 @@
import AssetDataEntry from "/imports/ui/Assets/AssetDataEntry.svelte"; import AssetDataEntry from "/imports/ui/Assets/AssetDataEntry.svelte";
import {useTracker} from "meteor/rdb:svelte-meteor-data"; import {useTracker} from "meteor/rdb:svelte-meteor-data";
import Assign from "/imports/ui/Assets/Assign.svelte"; import Assign from "/imports/ui/Assets/Assign.svelte";
import Assignments from "/imports/ui/Assets/Assignments.svelte";
let canManageLaptops = false; let canManageLaptops = false;
let isAdmin = false; let isAdmin = false;
@@ -22,6 +23,7 @@
let tabs = []; let tabs = [];
if(canManageLaptops) { if(canManageLaptops) {
tabs.push({id: 'assignments', label: 'Assignments'});
tabs.push({id: 'assignment', label: 'Assign'}); tabs.push({id: 'assignment', label: 'Assign'});
} }
if(isAdmin) { if(isAdmin) {
@@ -43,6 +45,8 @@
<AssetDataEntry></AssetDataEntry> <AssetDataEntry></AssetDataEntry>
{:else if activeTab && activeTab.id === 'assignment'} {:else if activeTab && activeTab.id === 'assignment'}
<Assign></Assign> <Assign></Assign>
{:else if activeTab && activeTab.id === 'assignments'}
<Assignments></Assignments>
{/if} {/if}
</div> </div>

View File

@@ -37,6 +37,8 @@
let selectedGrade = 'All'; let selectedGrade = 'All';
let selectedAssignee; let selectedAssignee;
let assetId = ""; let assetId = "";
let assetIdWidget;
$: { $: {
if(selectedSiteId) { if(selectedSiteId) {
if(selectedCategory === 'Student') { if(selectedCategory === 'Student') {
@@ -52,6 +54,13 @@
} }
} }
} }
const createAssignment = () => {
if(assetId && assetId.length && selectedAssignee) {
Meteor.call("AssetAssignments.add", assetId, selectedCategory === 'Student' ? "Student" : "Staff", selectedAssignee._id)
assetId = "";
}
}
</script> </script>
<div class="container"> <div class="container">
@@ -92,9 +101,14 @@
</LayoutGrid> </LayoutGrid>
</Paper> </Paper>
<TextField type="text" style="width: 100%" bind:value={assetId} label="Asset ID"> <div style="width: 100%; display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; align-items: flex-end; align-content: stretch; column-gap: 2rem;">
<TextField bind:this={assetIdWidget} style="flex-grow: 999;" type="text" bind:value={assetId} label="Asset ID">
</TextField> </TextField>
<Button variant="raised" color="secondary" on:click={createAssignment()} disabled={!assetId || assetId.length === 0 || !selectedAssignee}>
<Label style="color: white">Create</Label>
</Button>
</div>
<List twoLine singleSelection style="max-height: 60%"> <List twoLine singleSelection style="max-height: 60%">
{#if $assignees} {#if $assignees}
{#each $assignees as assignee} {#each $assignees as assignee}

View File

@@ -0,0 +1,133 @@
<script>
import {Meteor} from "meteor/meteor";
import {onMount} from "svelte";
import {Sites} from "../../api/sites";
import GridTable from "./../GridTable.svelte";
import {writable} from "svelte/store";
import TextField from '@smui/textfield';
import HelperText from '@smui/textfield/helper-text';
import {Students} from "../../api/students";
import Select, { Option } from '@smui/select';
import Dialog, { Title, Content, Actions } from '@smui/dialog';
import Button, { Label } from '@smui/button';
import {Staff} from "/imports/api/staff";
import List, {Item, Graphic, Meta, Text, PrimaryText, SecondaryText} from '@smui/list';
import Paper from '@smui/paper';
import LayoutGrid, {Cell} from '@smui/layout-grid';
let grades = ['Staff', 'All Grades'];
onMount(async () => {
Meteor.subscribe('sites');
Meteor.call('students.getPossibleGrades', (err, result) => {
if(err) console.log(err);
else {
grades = ['Staff', 'All Grades', ...result];
}
});
});
// Load the sites (reactive).
let sites = Sites.find({});
let selectedSiteId;
let categories = ['Email', 'First Name', 'Last Name'];
let selectedCategory = 'Email';
let selectedGrade = 'All';
let searchText = "";
let searchResults = [];
let selectedResult;
$: {
if(selectedSiteId) {
Meteor.subscribe('students', selectedSiteId);
Meteor.subscribe('staff', selectedSiteId);
}
}
$: {
selectedResult = null;
// Require at least two characters in the search field before we start filtering.
if(selectedSiteId && selectedGrade && selectedCategory && searchText && searchText.length > 1) {
let query = {};
if(selectedCategory === 'Email') {
query.email = {$regex: searchText, $options: 'i'};
}
else if(selectedCategory === 'First Name') {
query.firstName = {$regex: searchText, $options: 'i'};
}
else {
query.lastName = {$regex: searchText, $options: 'i'};
}
if(selectedCategory === "Staff") {
searchResults = Staff.find(query);
}
else {
if(selectedGrade !== 'All') {
query.grade = selectedGrade;
}
searchResults = Students.find(query).fetch();
}
}
else {
searchResults = [];
}
}
</script>
<div class="container">
<h1 style="display: block">Asset Assignments</h1>
<Paper>
<LayoutGrid>
<Cell span="{6}">
<Select bind:value={selectedSiteId} label="Site">
{#each $sites as site}
<Option value={site._id}>{site.name}</Option>
{/each}
</Select>
</Cell>
<Cell span="{6}">
<Select bind:value={selectedGrade} label="Grade">
{#each grades as grade}
<Option value={grade}>{grade}</Option>
{/each}
</Select>
</Cell>
<Cell span="{6}">
<Select bind:value={selectedCategory} label="Category">
{#each categories as category}
<Option value={category}>{category}</Option>
{/each}
</Select>
</Cell>
<Cell span="{6}">
<TextField type="text" bind:value={searchText} label="Search"></TextField>
</Cell>
</LayoutGrid>
</Paper>
<div style="width: 100%; display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; align-items: flex-end; align-content: stretch; column-gap: 2rem;">
<!-- <TextField bind:this={assetIdWidget} style="flex-grow: 999;" type="text" bind:value={assetId} label="Asset ID">-->
<!-- </TextField>-->
<!-- <Button variant="raised" color="secondary" on:click={createAssignment()} disabled={!assetId || assetId.length === 0 || !selectedAssignee}>-->
<!-- <Label style="color: white">Create</Label>-->
<!-- </Button>-->
</div>
<List twoLine singleSelection>
{#each searchResults as result}
<Item selected={selectedResult === result}>
<Text>
<PrimaryText>{result.firstName} {result.lastName}</PrimaryText>
<SecondaryText>{result.email} {result.grade ? '(' + result.grade + ')' : ""}</SecondaryText>
</Text>
</Item>
{/each}
</List>
</div>
<style>
</style>