import { Meteor } from 'meteor/meteor'; import React, { useState } from 'react'; import { useTracker } from 'meteor/react-meteor-data'; import _ from 'lodash'; import SimpleTable from "/imports/ui/util/SimpleTable"; import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; import Select from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import {Students} from "/imports/api/students"; import {Sites} from "/imports/api/sites"; import Box from "@mui/material/Box"; import ToggleButtonGroup from "@mui/material/ToggleButtonGroup"; import ToggleButton from "@mui/material/ToggleButton"; import {InputLabel, List, ListItemButton, ListItemText, Switch} from "@mui/material"; import FormControl from "@mui/material/FormControl"; import Dialog from "@mui/material/Dialog"; import DialogTitle from "@mui/material/DialogTitle"; import DialogContent from "@mui/material/DialogContent"; import DialogActions from "@mui/material/DialogActions"; import FormControlLabel from "@mui/material/FormControlLabel"; import Checkbox from "@mui/material/Checkbox"; const cssSitesSelect = { margin: '0.6rem 0', minWidth: '20rem', } const cssFieldColumnContainer = { display: 'flex', flexDirection: 'column', backgroundColor: '#DDD', padding: '0.5rem', border: '1px solid #999', borderRadius: '0.2rem' } const cssGridFieldContainer = { display: 'grid', gridTemplateColumns: "1fr 1fr 1fr 1fr", columnGap: '1rem', rowGap: '0.4rem', marginBottom: '1.5rem' } const cssButtonContainer = { display: 'flex', gap: '1rem', justifyContent: 'flex-end' } const StudentEditor = ({value, close, defaultSiteId}) => { const [email, setEmail] = useState(value.email || "") const [id, setId] = useState(value.id || "") const [firstName, setFirstName] = useState(value.firstName || "") const [firstNameAlias, setFirstNameAlias] = useState(value.firstNameAlias || "") const [lastName, setLastName] = useState(value.lastName || "") const [grade, setGrade] = useState(value.grade || "") const [active, setActive] = useState(value.active) const [siteId, setSiteId] = useState(value.siteId ? value.siteId : defaultSiteId) const {sites} = useTracker(() => { let sites = Sites.find({}).fetch(); return {sites} }); if(!siteId && sites && sites.length > 0) { setSiteId(sites[0]._id) } const applyChanges = () => { close() //TODO Should invert this and only close if there was success on the server. if(value._id) Meteor.call("students.update", value._id, id, firstName, firstNameAlias, lastName, email, siteId, grade, active); else Meteor.call("students.add", id, firstName, firstNameAlias, lastName, email, siteId, grade, active); } const rejectChanges = () => { close() } return (

Student Editor

{setId(e.target.value)}}/> {setEmail(e.target.value)}}/> {setGrade(e.target.value)}}/> {setActive(e.target.checked)}}/>} label="Active"/> {setFirstName(e.target.value)}}/> {setFirstNameAlias(e.target.value)}}/> {setLastName(e.target.value)}}/> {setSiteId(e.target.value)}}> {sites.map((next, i) => { return {next.name} })}
) } export default () => { const siteAll = {_id: 0, name: "All"} const [site, setSite] = useState(siteAll._id) Meteor.subscribe('sites'); Meteor.subscribe('students'); const {sites} = useTracker(() => { const sites = Sites.find({}).fetch(); sites.push(siteAll); return {sites} }); const ACTIVE_BOTH = "both" const ACTIVE_ONLY = "active" const ACTIVE_OFF = "inactive" const [active, setActive] = useState(ACTIVE_BOTH) const [nameSearch, setNameSearch] = useState("") const {students} = useTracker(() => { const studentQuery = site === siteAll._id ? {} : {siteId: site} if(active !== ACTIVE_BOTH) { studentQuery["active"] = active === ACTIVE_ONLY } if(nameSearch && nameSearch.length > 2) { studentQuery["$or"] = [{firstName: {$regex: nameSearch, $options: 'i'}}, {firstNameAlias: {$regex: nameSearch, $options: 'i'}}, {lastName: {$regex: nameSearch, $options: 'i'}}] } let students = Students.find(studentQuery).fetch(); return {students} }); const columns = [ { name: "ID", value: (row) => row.id, descendingComparator: (a, b) => { if(a.id === b.id) return 0 else if(!a.id) return -1 else if(!b.id) return 1 else if(a.id.length < b.id.length) return 1 else if(a.id.length > b.id.length) return -1 else if(b.id < a.id) return -1 else return 1 } }, { name: "Email", value: (row) => row.email, }, { name: "First Name", value: (row) => row.firstName, }, { name: "Alias", value: (row) => row.firstNameAlias, }, { name: "Last Name", value: (row) => row.lastName, }, { name: "GRD", value: (row) => row.grade, descendingComparator: (a, b) => { if(a.grade === b.grade) return 0 else if(!a.grade) return -1 else if(!b.grade) return 1 else if(a.grade.length < b.grade.length) return 1 else if(a.grade.length > b.grade.length) return -1 else if(b.grade < a.grade) return -1 else return 1 } }, { name: "Active", value: (row) => row.active ? "Active" : "Inactive", }, ] const options = { key: (row) => row._id, editor: (row, close) => {return ()}, add: true, maxHeight: '40rem', keyHandler: (e, selected) => { if(selected && selected._id && e.key === "Delete") { Meteor.call("students.remove", selected._id); } } } const importData = (type) => { let input = document.createElement('input') input.type = 'file' input.onchange = _ => { let files = Array.from(input.files) if(files.length === 1) { let reader = new FileReader() reader.onload = () => { Meteor.call("students.loadCsv", reader.result, type, testImportOnly, (err, result) => { //Note: It would be nice to have feedback about the operation, but right now I cannot figure out how to wait on the server for the result of the callback that is wrapped by a bindEnvironment call. if(err) console.log(err) // else if(testImportOnly) console.log(result) }) } reader.readAsText(input.files[0]) } } input.click() } const [showImportDialog, setShowImportDialog] = useState(false) const [testImportOnly, setTestImportOnly] = useState(false) const openImportDialog = () => { setTestImportOnly(false) setShowImportDialog(true) } const closeImportDialog = (cause, importType) => { if(importType) importData(importType) if(showImportDialog) setShowImportDialog(false) } return ( <> Import

Imports students for the entire district and deals with altering the "active" flag for students no longer in the district or who have graduated.

Middle of Year

LIST STU SC ID SEM FN LN GR FNA

Run any time during the year to capture changes in student body.

End of Year

LIST STU NS ID SEM FN LN NG FNA IF NG >= 12

Use this query for the import if the school year is over (before summer school). It utilizes the next school & next grade fields instead of the current grade/school.

setTestImportOnly(e.target.checked)}/>} label="Test Only"/>
{setSite(e.target.value)}}> {sites.map((next, i) => { return {next.name} })} Active Students {setActive(e.target.value)}} aria-label="Active Students"> All Active Inactive {setNameSearch(e.target.value)}}/> ) }