Added a data entry page with initial content.
This commit is contained in:
@@ -21,7 +21,6 @@ shell-server@0.5.0 # Server-side component of the `meteor shell` comm
|
|||||||
#aldeed:collection2 # Attaches a schema to a collection
|
#aldeed:collection2 # Attaches a schema to a collection
|
||||||
#aldeed:schema-index # Allows the schema to specify fields to be indexed
|
#aldeed:schema-index # Allows the schema to specify fields to be indexed
|
||||||
|
|
||||||
svelte:compiler
|
|
||||||
#static-html@1.3.2
|
#static-html@1.3.2
|
||||||
rdb:svelte-meteor-data
|
rdb:svelte-meteor-data
|
||||||
accounts-ui@1.4.2
|
accounts-ui@1.4.2
|
||||||
@@ -32,7 +31,8 @@ accounts-google@1.4.0
|
|||||||
service-configuration@1.3.0
|
service-configuration@1.3.0
|
||||||
google-config-ui@1.0.3 # Adds the UI for logging in via Google
|
google-config-ui@1.0.3 # Adds the UI for logging in via Google
|
||||||
alanning:roles # Adds roles to the user
|
alanning:roles # Adds roles to the user
|
||||||
#zodern:melte
|
|
||||||
|
|
||||||
#babrahams:constellation # Alternative to MeteorToys - Has problems because it requires jquery 1.11.11 and everything else wants 2.x or 3.x
|
#babrahams:constellation # Alternative to MeteorToys - Has problems because it requires jquery 1.11.11 and everything else wants 2.x or 3.x
|
||||||
msavin:mongol # Free version of MeteorToys - Provides access to the client side MongoDB for debugging. (Ctrl-M to activate :: https://atmospherejs.com/msavin/mongol)
|
msavin:mongol # Free version of MeteorToys - Provides access to the client side MongoDB for debugging. (Ctrl-M to activate :: https://atmospherejs.com/msavin/mongol)
|
||||||
|
#zodern:melte # Alternative to meteor-svelte (https://github.com/meteor-svelte/meteor-svelte). Was more actively developed.
|
||||||
|
svelte:compiler # Switching back to this because of TS errors.
|
||||||
|
|||||||
@@ -52,10 +52,12 @@ Meteor.methods({
|
|||||||
check(assetId, String);
|
check(assetId, String);
|
||||||
check(serial, String);
|
check(serial, String);
|
||||||
|
|
||||||
|
// Convert the asset ID's to uppercase for storage to make searching easier.
|
||||||
|
assetId = assetId.toUpperCase();
|
||||||
|
|
||||||
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
||||||
let assetType = AssetTypes.findOne({assetTypeId});
|
let assetType = AssetTypes.findOne({assetTypeId});
|
||||||
|
|
||||||
if(assetType.hasSerial && serial || !assetType.hasSerial && !serial) {
|
|
||||||
if(serial) {
|
if(serial) {
|
||||||
Assets.insert({assetTypeId, assetId, serial});
|
Assets.insert({assetTypeId, assetId, serial});
|
||||||
}
|
}
|
||||||
@@ -63,20 +65,23 @@ Meteor.methods({
|
|||||||
Assets.insert({assetTypeId, assetId});
|
Assets.insert({assetTypeId, assetId});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
//Should never get here due to client side validation.
|
|
||||||
console.log("Error: Must provide a serial for asset types marked as having one, and may not provide one for asset types not marked as having one.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'assets.update'(_id, assetId, serial) {
|
'assets.update'(_id, assetId, serial) {
|
||||||
//TODO:
|
check(_id, String);
|
||||||
|
check(assetId, String);
|
||||||
|
if(serial) check(serial, String);
|
||||||
|
|
||||||
|
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
||||||
|
//TODO: Need to first verify there are no checked out assets to the staff member.
|
||||||
|
Assets.update({_id}, {$set: {assetId, serial}});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'assets.remove'(_id) {
|
'assets.remove'(_id) {
|
||||||
check(_id, String);
|
check(_id, String);
|
||||||
|
|
||||||
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
||||||
//TODO: Need to first verify there are no checked out assets to the staff member.
|
//TODO: Need to first verify there are no checked out assets to the staff member.
|
||||||
|
Assets.remove({_id});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -237,7 +237,7 @@
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>Sites</h2>
|
<h2>Sites</h2>
|
||||||
<GridTable bind:rows={sites} columns="{siteColumns}" actions="{actions}" rowKey="{(v) => {return v._id}}" bind:edited="{editedSite}" on:selection={onSiteSelection}>
|
<GridTable bind:rows={sites} columns="{siteColumns}" actions="{siteActions}" rowKey="{(v) => {return v._id}}" bind:edited="{editedSite}" on:selection={onSiteSelection}>
|
||||||
{#if dirtySite}
|
{#if dirtySite}
|
||||||
<div class="editorContainer">
|
<div class="editorContainer">
|
||||||
<div style="grid-column: 1/span 1">
|
<div style="grid-column: 1/span 1">
|
||||||
|
|||||||
@@ -1,164 +1,26 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import Tab, { Label } from '@smui/tab';
|
||||||
|
import TabBar from '@smui/tab-bar';
|
||||||
import {Meteor} from "meteor/meteor";
|
import {Meteor} from "meteor/meteor";
|
||||||
import {onMount} from "svelte";
|
import {onMount} from "svelte";
|
||||||
import {writable} from "svelte/store";
|
import AssetList from "/imports/ui/Assets/AssetList.svelte";
|
||||||
import TextField from '@smui/textfield';
|
import AssetDataEntry from "/imports/ui/Assets/AssetDataEntry.svelte";
|
||||||
import HelperText from '@smui/textfield/helper-text';
|
|
||||||
import Select, { Option } from '@smui/select';
|
|
||||||
import {AssetTypes} from "../api/asset-types";
|
|
||||||
import { useTracker } from 'meteor/rdb:svelte-meteor-data';
|
|
||||||
import GridTable from "./GridTable.svelte";
|
|
||||||
import {Assets} from "../api/assets";
|
|
||||||
|
|
||||||
onMount(async () => {
|
let activeTab = null;
|
||||||
Meteor.subscribe('assetTypes');
|
|
||||||
});
|
|
||||||
let assetTypes;
|
|
||||||
//$m: assetTypes = AssetTypes.find({}).fetch();
|
|
||||||
//$: assetTypes = useTracker(() => AssetTypes.find({}).fetch());
|
|
||||||
$: assetTypes = AssetTypes.find({});
|
|
||||||
|
|
||||||
let selectedType = null;
|
|
||||||
const selectAssetType = (assetType) => {
|
|
||||||
selectedType = assetType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asset Table //
|
|
||||||
|
|
||||||
const assetColumns = [
|
|
||||||
{
|
|
||||||
key: "assetId",
|
|
||||||
title: "Asset ID",
|
|
||||||
value: v => v.assetId,
|
|
||||||
minWidth: 100,
|
|
||||||
weight: 1,
|
|
||||||
cls: "assetId",
|
|
||||||
}, {
|
|
||||||
key: "serial",
|
|
||||||
title: "Serial",
|
|
||||||
value: v => v.serial,
|
|
||||||
minWidth: 100,
|
|
||||||
weight: 1,
|
|
||||||
cls: "serial",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const assetActions = {
|
|
||||||
title: "Actions",
|
|
||||||
headerWidgets: [
|
|
||||||
{icon: "add_box", action: () => {editedAsset.set({});}, tooltip: "Add a new asset."}
|
|
||||||
],
|
|
||||||
rowWidgets: [
|
|
||||||
{icon: "add_circle", action: (v) => {editedAsset.set(v)}},
|
|
||||||
{icon: "delete", action: (v) => {deleteAsset(v)}}
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const deleteAsset = asset => {
|
|
||||||
//TODO:
|
|
||||||
};
|
|
||||||
// Create a holder for the site being edited. This allows us to clear the editor when the user finishes, and allows the table or parent view to setup the editor.
|
|
||||||
let editedAsset = writable(null);
|
|
||||||
let dirtyAsset = null;
|
|
||||||
// Copy the edited site when ever it changes, set some defaults for a new site object (to make the view happy).
|
|
||||||
editedAsset.subscribe(site => {dirtyAsset = Object.assign({serial: "", assetId: "", assetTypeId: ""}, site)});
|
|
||||||
// Load the sites (reactive).
|
|
||||||
let assets = Assets.find({});
|
|
||||||
const applyAssetChanges = () => {
|
|
||||||
if(dirtyAsset._id)
|
|
||||||
Meteor.call("assets.update", dirtyAsset._id, dirtyAsset.serial, dirtyAsset.assetId);
|
|
||||||
else
|
|
||||||
Meteor.call("assets.add", dirtyAsset.serial, dirtyAsset.assetId, dirtyAsset.assetTypeId);
|
|
||||||
editedAsset.set(null);
|
|
||||||
}
|
|
||||||
const rejectAssetChanges = () => {
|
|
||||||
editedAsset.set(null);
|
|
||||||
}
|
|
||||||
let selectedAsset = null;
|
|
||||||
const onAssetSelection = (e) => {
|
|
||||||
selectedAsset = Assets.findOne({_id: e.detail});
|
|
||||||
}
|
|
||||||
|
|
||||||
// End Asset Table //
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="listContainer">
|
<TabBar tabs={[{id:'list', label:'Asset List'}, {id:'entry', label:'Data Entry'}]} minWidth let:tab bind:active={activeTab}>
|
||||||
<h2>Asset Types</h2>
|
<Tab {tab}>
|
||||||
<div class="list">
|
<Label>{tab.label}</Label>
|
||||||
{#each $assetTypes as type}
|
</Tab>
|
||||||
<div class="listItem" class:selected={selectedType === type} on:click={(e) => {selectAssetType(type)}}>
|
</TabBar>
|
||||||
<div class="listItemAssetTypeName">{type.name}</div>
|
{#if activeTab && activeTab.id === 'list'}
|
||||||
<div class="listItemAssetTypeDescription">{type.description}</div>
|
<AssetList></AssetList>
|
||||||
</div>
|
{:else if activeTab && activeTab.id === 'entry'}
|
||||||
{/each}
|
<AssetDataEntry></AssetDataEntry>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="assetsContainer">
|
|
||||||
<h2>Assets</h2>
|
|
||||||
<GridTable bind:rows={assets} columns="{assetColumns}" actions="{assetActions}" rowKey="{(v) => {return v._id}}" bind:edited="{editedAsset}" on:selection={onAssetSelection}>
|
|
||||||
{#if dirtyAsset}
|
|
||||||
<div class="editorContainer">
|
|
||||||
<Select bind:value={dirtyAsset.assetTypeId} label="Select Menu">
|
|
||||||
{#each $assetTypes as assetType}
|
|
||||||
<Option value={assetType._id}>{assetType.name}</Option>
|
|
||||||
{/each}
|
|
||||||
</Select>
|
|
||||||
<div style="grid-column: 1/span 1">
|
|
||||||
<TextField type="text" style="width: 100%" bind:value={dirtyAsset.assetId} label="AssetId">
|
|
||||||
<HelperText slot="helper">Provide a unique asset ID string.</HelperText>
|
|
||||||
</TextField>
|
|
||||||
</div>
|
|
||||||
<div style="grid-column: 1/span 1">
|
|
||||||
<TextField type="text" style="width: 100%" bind:value={dirtyAsset.serial} label="Serial">
|
|
||||||
<HelperText slot="helper">Provide a unique serial string for the asset.</HelperText>
|
|
||||||
</TextField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="button" style="grid-column: 2/span 1;" class="button accept-button material-icons material-symbols-outlined" on:click={applyAssetChanges}>
|
|
||||||
check
|
|
||||||
</button>
|
|
||||||
<button type="button" style="grid-column: 3/span 1;" class="button reject-button material-icons material-symbols-outlined" on:click={rejectAssetChanges}>
|
|
||||||
close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</GridTable>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.listContainer {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 1rem;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
div.listContainer h2 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.list {
|
|
||||||
border: 2px solid #4f4e4e;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
.listItem:nth-child(even) {
|
|
||||||
/*background: #f6f6f6;*/
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
.listItem {
|
|
||||||
margin: 4px 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
user-select: none;
|
|
||||||
border-bottom: 1px solid gray;
|
|
||||||
}
|
|
||||||
.listItem:last-child {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
.listItem.selected {
|
|
||||||
background-color: rgba(255, 255, 0, 0.38);
|
|
||||||
}
|
|
||||||
|
|
||||||
.assetsContainer {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
172
imports/ui/Assets/AssetDataEntry.svelte
Normal file
172
imports/ui/Assets/AssetDataEntry.svelte
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<script>
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
import {writable} from "svelte/store";
|
||||||
|
import Select, { Option } from '@smui/select';
|
||||||
|
import Dialog, { Title, Content, Actions } from '@smui/dialog';
|
||||||
|
import Button, { Label } from '@smui/button';
|
||||||
|
import List, {Item,Text,PrimaryText,SecondaryText} from '@smui/list';
|
||||||
|
import TextField from '@smui/textfield';
|
||||||
|
import u from 'umbrellajs';
|
||||||
|
import {AssetTypes} from "/imports/api/asset-types";
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
Meteor.subscribe('assetTypes');
|
||||||
|
Meteor.subscribe('assets');
|
||||||
|
});
|
||||||
|
let assetTypes;
|
||||||
|
//$m: assetTypes = AssetTypes.find({}).fetch();
|
||||||
|
//$: assetTypes = useTracker(() => AssetTypes.find({}).fetch());
|
||||||
|
$: assetTypes = AssetTypes.find({});
|
||||||
|
let selectedAssetTypes = [];
|
||||||
|
|
||||||
|
let selectedType = null;
|
||||||
|
const selectAssetType = (assetType) => {
|
||||||
|
selectedType = assetType;
|
||||||
|
}
|
||||||
|
const showAddAssetType = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
let openAddAssetTypeButton;
|
||||||
|
let isOpen_AddAssetType = false;
|
||||||
|
const openAddAssetType = () => {
|
||||||
|
isOpen_AddAssetType = !isOpen_AddAssetType;
|
||||||
|
//openAddAssetTypeButton.innerHTML = isOpen_AddAssetType ? "close" : "add";
|
||||||
|
|
||||||
|
if(isOpen_AddAssetType) {
|
||||||
|
openAddAssetTypeButton.classList.add("reject-button");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
openAddAssetTypeButton.classList.remove("reject-button");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let selectedAddAssetType = null;
|
||||||
|
|
||||||
|
let openDialog = false;
|
||||||
|
let assetTypeDialogSelectedIndex;
|
||||||
|
let assetTypeDialogSelectedValue;
|
||||||
|
const openAssetTypesDialog = () => {
|
||||||
|
assetTypeDialogSelectedIndex = -1;
|
||||||
|
openDialog = true;
|
||||||
|
}
|
||||||
|
const dialogClosed = (e) => {
|
||||||
|
switch(e.detail.action) {
|
||||||
|
case 'accept':
|
||||||
|
if(!selectedAssetTypes.some(e => e._id === assetTypeDialogSelectedValue._id)) {
|
||||||
|
selectedAssetTypes = [...selectedAssetTypes, assetTypeDialogSelectedValue];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 'cancel':
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let assetId = "";
|
||||||
|
let serial = "";
|
||||||
|
const addAsset = () => {
|
||||||
|
Meteor.call("assets.add", selectedAssetType._id, assetId, serial);
|
||||||
|
assetId = "";
|
||||||
|
serial = "";
|
||||||
|
}
|
||||||
|
let selectedAssetType = null;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="listContainer">
|
||||||
|
<Dialog bind:open={openDialog} surface$style="width: 850px; max-width: calc(100vw - 32px);" on:SMUIDialog:closed={dialogClosed}>
|
||||||
|
<Title id="large-scroll-title">Asset Types</Title>
|
||||||
|
<Content id="large-scroll-content">
|
||||||
|
<List class="assetTypesList" twoLine singleSelection bind:selectedIndex={assetTypeDialogSelectedIndex}>
|
||||||
|
{#each $assetTypes as type}
|
||||||
|
<Item on:SMUI:action={() => (assetTypeDialogSelectedValue = type)} selected={assetTypeDialogSelectedValue === type}>
|
||||||
|
<Text>
|
||||||
|
<PrimaryText>{type.name}</PrimaryText>
|
||||||
|
<SecondaryText>{type.description}</SecondaryText>
|
||||||
|
</Text>
|
||||||
|
</Item>
|
||||||
|
{/each}
|
||||||
|
</List>
|
||||||
|
</Content>
|
||||||
|
<Actions>
|
||||||
|
<Button action="accept" default>
|
||||||
|
<Label>Ok</Label>
|
||||||
|
</Button>
|
||||||
|
<Button action="cancel">
|
||||||
|
<Label>Cancel</Label>
|
||||||
|
</Button>
|
||||||
|
</Actions>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<div class="columnContainer">
|
||||||
|
<div class="listColumn">
|
||||||
|
<h3 style="display: inline-block">Asset Types</h3>
|
||||||
|
<Button class="addBtn" on:click={openAssetTypesDialog}>
|
||||||
|
<Label>Add...</Label>
|
||||||
|
</Button>
|
||||||
|
<List class="assetTypeList" singleSelection dense>
|
||||||
|
{#each selectedAssetTypes as type}
|
||||||
|
<Item on:SMUI:action={() => (selectedAssetType = type)} selected={selectedAssetType === type}>
|
||||||
|
<Text>{type.name}</Text>
|
||||||
|
</Item>
|
||||||
|
{/each}
|
||||||
|
</List>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="fieldColumn">
|
||||||
|
<div style="grid-column: 1/span 1">
|
||||||
|
<TextField type="text" style="width: 100%" bind:value={assetId} label="AssetId">
|
||||||
|
</TextField>
|
||||||
|
</div>
|
||||||
|
<div style="grid-column: 1/span 1">
|
||||||
|
<TextField type="text" style="width: 100%" bind:value={serial} label="Serial">
|
||||||
|
</TextField>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button on:click={addAsset}>Add</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.columnContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(20rem, 1fr) minmax(20rem, 1fr);
|
||||||
|
}
|
||||||
|
:global(.addBtn) {
|
||||||
|
margin-left: 4rem;
|
||||||
|
}
|
||||||
|
:global(.assetTypeList) {
|
||||||
|
--mdc-ripple-color: blue;
|
||||||
|
--mdc-ripple-selected-opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listContainer {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 1rem;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
div.listContainer h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
//border: 2px solid #4f4e4e;
|
||||||
|
}
|
||||||
|
.listItem:nth-child(even) {
|
||||||
|
/*background: #f6f6f6;*/
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
.listItem {
|
||||||
|
margin: 4px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
padding-top: 0.4rem;
|
||||||
|
border-bottom: 1px solid gray;
|
||||||
|
}
|
||||||
|
.listItem:last-child {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.listItem.selected {
|
||||||
|
background-color: rgba(255, 255, 0, 0.38);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
165
imports/ui/Assets/AssetList.svelte
Normal file
165
imports/ui/Assets/AssetList.svelte
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<script>
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
import {writable} from "svelte/store";
|
||||||
|
import TextField from '@smui/textfield';
|
||||||
|
import HelperText from '@smui/textfield/helper-text';
|
||||||
|
import Select, { Option } from '@smui/select';
|
||||||
|
import {AssetTypes} from "../../api/asset-types";
|
||||||
|
import { useTracker } from 'meteor/rdb:svelte-meteor-data';
|
||||||
|
import GridTable from "./../GridTable.svelte";
|
||||||
|
import {Assets} from "../../api/assets";
|
||||||
|
import u from 'umbrellajs';
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
Meteor.subscribe('assetTypes');
|
||||||
|
Meteor.subscribe('assets');
|
||||||
|
});
|
||||||
|
let assetTypes;
|
||||||
|
$: assetTypes = AssetTypes.find({});
|
||||||
|
|
||||||
|
// Asset Table //
|
||||||
|
|
||||||
|
const assetColumns = [
|
||||||
|
{
|
||||||
|
key: "assetId",
|
||||||
|
title: "Asset ID",
|
||||||
|
value: v => v.assetId,
|
||||||
|
minWidth: 100,
|
||||||
|
weight: 1,
|
||||||
|
cls: "assetId",
|
||||||
|
}, {
|
||||||
|
key: "serial",
|
||||||
|
title: "Serial",
|
||||||
|
value: v => v.serial ? v.serial : "-",
|
||||||
|
minWidth: 100,
|
||||||
|
weight: 1,
|
||||||
|
cls: "serial",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const assetActions = {
|
||||||
|
title: "",
|
||||||
|
headerWidgets: [
|
||||||
|
{icon: "add_box", action: () => {editedAsset.update((v) => {return v ? null : {}})}, tooltip: "Add a new asset."}
|
||||||
|
],
|
||||||
|
rowWidgets: [
|
||||||
|
{icon: "add_circle", action: (v) => {editedAsset.set(v)}},
|
||||||
|
{icon: "delete", action: (v) => {deleteAsset(v)}}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const deleteAsset = asset => {
|
||||||
|
if(asset && asset._id) Meteor.call("assets.remove", asset._id);
|
||||||
|
};
|
||||||
|
// Create a holder for the site being edited. This allows us to clear the editor when the user finishes, and allows the table or parent view to setup the editor.
|
||||||
|
let editedAsset = writable(null);
|
||||||
|
let dirtyAsset = null;
|
||||||
|
// Copy the edited site when ever it changes, set some defaults for a new site object (to make the view happy).
|
||||||
|
editedAsset.subscribe(site => {
|
||||||
|
if(site) {
|
||||||
|
dirtyAsset = Object.assign({serial: "", assetId: "", assetTypeId: ""}, site);
|
||||||
|
//document.getElementsByClassName('select').focus();
|
||||||
|
}
|
||||||
|
else dirtyAsset = null;
|
||||||
|
});
|
||||||
|
// Load the sites (reactive).
|
||||||
|
let assets = Assets.find({});
|
||||||
|
const applyAssetChanges = () => {
|
||||||
|
if(dirtyAsset._id)
|
||||||
|
Meteor.call("assets.update", dirtyAsset._id, dirtyAsset.assetId, dirtyAsset.serial);
|
||||||
|
else
|
||||||
|
Meteor.call("assets.add", dirtyAsset.assetTypeId, dirtyAsset.assetId, dirtyAsset.serial);
|
||||||
|
editedAsset.set(null);
|
||||||
|
}
|
||||||
|
const rejectAssetChanges = () => {
|
||||||
|
editedAsset.set(null);
|
||||||
|
}
|
||||||
|
let selectedAsset = null;
|
||||||
|
const onAssetSelection = (e) => {
|
||||||
|
selectedAsset = Assets.findOne({_id: e.detail});
|
||||||
|
}
|
||||||
|
let editorFirstComponent = null;
|
||||||
|
$: if(editorFirstComponent) editorFirstComponent.focus();
|
||||||
|
const initEditor = (e) => {
|
||||||
|
//editorFirstComponent.focus();
|
||||||
|
//document.getElementsByClassName("focusMe")[0].children[0].children[2].focus();
|
||||||
|
// u('.focusMe .mdc-select__selected-text').first().focus();
|
||||||
|
//$('.select').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// End Asset Table //
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="assetsContainer">
|
||||||
|
<h2>Assets</h2>
|
||||||
|
<GridTable bind:rows={assets} columns="{assetColumns}" actions="{assetActions}" rowKey="{(v) => {return v._id}}" bind:edited="{editedAsset}" on:selection={onAssetSelection}>
|
||||||
|
{#if dirtyAsset}
|
||||||
|
<div class="editorContainer" use:initEditor>
|
||||||
|
<div style="grid-column: 1/span 1">
|
||||||
|
<Select class="focusMe select" bind:value={dirtyAsset.assetTypeId} label="Asset Type" bind:this={editorFirstComponent}>
|
||||||
|
{#each $assetTypes as assetType}
|
||||||
|
<Option value={assetType._id}>{assetType.name}</Option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div style="grid-column: 1/span 1">
|
||||||
|
<TextField type="text" style="width: 100%" bind:value={dirtyAsset.assetId} label="AssetId">
|
||||||
|
</TextField>
|
||||||
|
</div>
|
||||||
|
<div style="grid-column: 1/span 1">
|
||||||
|
<TextField type="text" style="width: 100%" bind:value={dirtyAsset.serial} label="Serial">
|
||||||
|
</TextField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="grid-column: 1/span 1; white-space: nowrap; text-align: right; margin-top: 0.5rem">
|
||||||
|
<button type="button" style="grid-column: 2/span 1;" class="button accept-button material-icons material-symbols-outlined" on:click={applyAssetChanges}>
|
||||||
|
check
|
||||||
|
</button>
|
||||||
|
<button type="button" style="grid-column: 3/span 1;" class="button reject-button material-icons material-symbols-outlined" on:click={rejectAssetChanges}>
|
||||||
|
close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</GridTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.assetsContainer {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.editorContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(10px, 1fr);
|
||||||
|
column-gap: 2rem;
|
||||||
|
background-color: #e0e3e7;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
:global(.select) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.accept-button, .reject-button {
|
||||||
|
font-size: .8rem;
|
||||||
|
padding: .6rem;
|
||||||
|
margin: auto;
|
||||||
|
color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 0;
|
||||||
|
font-weight: 800;
|
||||||
|
alignment: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.accept-button {
|
||||||
|
background-color: rgba(61, 148, 61, 0.91);
|
||||||
|
}
|
||||||
|
.reject-button {
|
||||||
|
background-color: rgba(176, 64, 64, 0.61);
|
||||||
|
}
|
||||||
|
.accept-button:hover {
|
||||||
|
background-color: rgb(61, 148, 61);
|
||||||
|
}
|
||||||
|
.reject-button:hover {
|
||||||
|
background-color: rgb(176, 64, 64);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
41
package-lock.json
generated
41
package-lock.json
generated
@@ -1370,7 +1370,7 @@
|
|||||||
"check-error": {
|
"check-error": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
||||||
"integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
|
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
@@ -1420,7 +1420,7 @@
|
|||||||
"color-name": {
|
"color-name": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
},
|
},
|
||||||
"color-string": {
|
"color-string": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
@@ -1443,7 +1443,7 @@
|
|||||||
"connect-route": {
|
"connect-route": {
|
||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz",
|
||||||
"integrity": "sha512-6OZGYeoKS7zzhCAGP+Du7WYjP4geK3ii0sZlS+Oqx+ANFPcS5t/jDJr9BuhgN9qPz1ld8iahiPxNrgJyHeZ3cw=="
|
"integrity": "sha1-48IYMZ0uiKiprgsOD+Cacpw5dEo="
|
||||||
},
|
},
|
||||||
"dayjs": {
|
"dayjs": {
|
||||||
"version": "1.11.3",
|
"version": "1.11.3",
|
||||||
@@ -1535,7 +1535,7 @@
|
|||||||
"get-func-name": {
|
"get-func-name": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||||
"integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
|
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"glob-parent": {
|
"glob-parent": {
|
||||||
@@ -2493,19 +2493,19 @@
|
|||||||
"tr46": {
|
"tr46": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"webidl-conversions": {
|
"webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"whatwg-url": {
|
"whatwg-url": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"tr46": "~0.0.3",
|
"tr46": "~0.0.3",
|
||||||
@@ -2586,7 +2586,7 @@
|
|||||||
"require-directory": {
|
"require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"rollup-plugin-css-only": {
|
"rollup-plugin-css-only": {
|
||||||
@@ -2631,7 +2631,7 @@
|
|||||||
"simple-swizzle": {
|
"simple-swizzle": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-arrayish": "^0.3.1"
|
"is-arrayish": "^0.3.1"
|
||||||
}
|
}
|
||||||
@@ -2670,7 +2670,7 @@
|
|||||||
"sparse-bitfield": {
|
"sparse-bitfield": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||||
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
|
"integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"memory-pager": "^1.0.2"
|
"memory-pager": "^1.0.2"
|
||||||
@@ -2679,7 +2679,7 @@
|
|||||||
"stack-trace": {
|
"stack-trace": {
|
||||||
"version": "0.0.10",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||||
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="
|
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
|
||||||
},
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
@@ -2710,9 +2710,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"svelte": {
|
"svelte": {
|
||||||
"version": "3.48.0",
|
"version": "3.49.0",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.48.0.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.49.0.tgz",
|
||||||
"integrity": "sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ=="
|
"integrity": "sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA=="
|
||||||
},
|
},
|
||||||
"svelte-material-ui": {
|
"svelte-material-ui": {
|
||||||
"version": "6.0.0-beta.16",
|
"version": "6.0.0-beta.16",
|
||||||
@@ -2814,6 +2814,17 @@
|
|||||||
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "4.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||||
|
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"umbrellajs": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/umbrellajs/-/umbrellajs-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-3Y4LVTFZWyXdSc7jSiJQziZJ77R2I9YwO5kaKZeHlXfH3clzG0TsHNMQrKNJqKDlWkORN8QEvqrVOKhjVUN6IQ=="
|
||||||
|
},
|
||||||
"underscore": {
|
"underscore": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
|
||||||
@@ -2822,7 +2833,7 @@
|
|||||||
"util-deprecate": {
|
"util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
},
|
},
|
||||||
"webidl-conversions": {
|
"webidl-conversions": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"svelte": "^3.46.4",
|
"svelte": "^3.46.4",
|
||||||
"svelte-material-ui": "^6.0.0-beta.16",
|
"svelte-material-ui": "^6.0.0-beta.16",
|
||||||
"tinro": "^0.6.12",
|
"tinro": "^0.6.12",
|
||||||
|
"umbrellajs": "^3.3.1",
|
||||||
"underscore": "^1.13.2",
|
"underscore": "^1.13.2",
|
||||||
"winston": "^3.7.2",
|
"winston": "^3.7.2",
|
||||||
"winston-daily-rotate-file": "^4.6.1",
|
"winston-daily-rotate-file": "^4.6.1",
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
"svelte",
|
"svelte",
|
||||||
"html"
|
"html"
|
||||||
],
|
],
|
||||||
"hydratable": true,
|
"hydratable": false,
|
||||||
"css": true
|
"css": true
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -56,6 +57,7 @@
|
|||||||
"@smui/textfield": "^6.0.0-beta.16",
|
"@smui/textfield": "^6.0.0-beta.16",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"rollup-plugin-css-only": "^3.1.0",
|
"rollup-plugin-css-only": "^3.1.0",
|
||||||
"smui-theme": "^6.0.0-beta.16"
|
"smui-theme": "^6.0.0-beta.16",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
tsconfig.json
Normal file
40
tsconfig.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Basic Options */
|
||||||
|
"target": "es2018",
|
||||||
|
"module": "esNext",
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": false,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
|
||||||
|
/* Additional Checks */
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noImplicitReturns": false,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
|
||||||
|
/* Module Resolution Options */
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
/* Support absolute /imports/* with a leading '/' */
|
||||||
|
"/*": ["*"]
|
||||||
|
},
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"types": ["node", "mocha"],
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"preserveSymlinks": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"./.meteor/**",
|
||||||
|
"./packages/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user