();
const theme = useTheme();
const [openTransaction, setOpenTransaction] = useState(false);
+ const [{ user }] = useGlobalState();
useEffect(() => {
setFillLevel((grainBag.bushels() / grainBag.capacity()) * 100);
@@ -83,13 +84,13 @@ export default function GrainBagVisualizer(props: Props) {
const grainOverlay = () => {
let displayPending = pendingGrainAmount;
let displayDiff = grainDiff;
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
if (displayPending && displayDiff) {
displayPending = Math.round((displayPending / grainBag.bushelsPerTonne()) * 100) / 100;
displayDiff = Math.round((displayDiff / grainBag.bushelsPerTonne()) * 100) / 100;
}
}
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
if (displayPending && displayDiff) {
displayPending = Math.round((displayPending / (grainBag.bushelsPerTonne() * 0.907)) * 100) / 100;
displayDiff = Math.round((displayDiff / (grainBag.bushelsPerTonne()* 0.907)) * 100) / 100;
@@ -148,7 +149,7 @@ export default function GrainBagVisualizer(props: Props) {
};
const grainAmountDisplay = () => {
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
return (
@@ -157,7 +158,7 @@ export default function GrainBagVisualizer(props: Props) {
({grainBag.fillPercent()}%)
);
- } else if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
+ } else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
return (
diff --git a/src/interactions/InteractionSettings.tsx b/src/interactions/InteractionSettings.tsx
index 87bdf98..4f2a2d3 100644
--- a/src/interactions/InteractionSettings.tsx
+++ b/src/interactions/InteractionSettings.tsx
@@ -195,7 +195,9 @@ export default function InteractionSettings(props: Props) {
let value = describeMeasurement(
condition.measurementType,
or(initialComponent, Component.create()).settings.type,
- or(initialComponent, Component.create()).settings.subtype
+ or(initialComponent, Component.create()).settings.subtype,
+ undefined,
+ user
).toDisplay(condition.value);
rawConditionValues.push(value.toString());
});
@@ -425,7 +427,7 @@ export default function InteractionSettings(props: Props) {
mappedComponents.get(componentIDToString(interaction.settings.source)),
Component.create()
);
- return describeMeasurement(measurementType, source.settings.type, source.settings.subtype);
+ return describeMeasurement(measurementType, source.settings.type, source.settings.subtype, undefined, user);
};
const describeSink = (): MeasurementDescriber => {
@@ -433,7 +435,9 @@ export default function InteractionSettings(props: Props) {
return describeMeasurement(
Measurement.boolean,
or(sink, Component.create()).settings.type,
- or(sink, Component.create()).settings.subtype
+ or(sink, Component.create()).settings.subtype,
+ undefined,
+ user
);
};
@@ -861,7 +865,7 @@ export default function InteractionSettings(props: Props) {
const conditionGroup = (index: number) => {
if (index >= numConditions) return;
let measurementType = interaction.settings.conditions[index].measurementType;
- let source = describeSource(measurementType);
+ let source = describeSource(measurementType, );
let isBoolean = isBooleanValue(index);
return (
diff --git a/src/interactions/InteractionsOverview.tsx b/src/interactions/InteractionsOverview.tsx
index 127f2a6..600c88a 100644
--- a/src/interactions/InteractionsOverview.tsx
+++ b/src/interactions/InteractionsOverview.tsx
@@ -71,7 +71,7 @@ interface Props {
export default function InteractionsOverview(props: Props) {
const classes = useStyles();
- const [{as}] = useGlobalState();
+ const [{as, user}] = useGlobalState();
const interactionsAPI = useInteractionsAPI();
const { success, error } = useSnackbar();
const { device, component, components, permissions, refreshCallback } = props;
@@ -118,12 +118,12 @@ export default function InteractionsOverview(props: Props) {
if (!conditions) return items;
conditions.forEach((condition: pond.IInteractionCondition, conditionIndex: number) => {
let measurementType = condition.measurementType;
- let measurement = describeMeasurement(condition.measurementType, sourceType, sourceSubtype);
+ let measurement = describeMeasurement(condition.measurementType, sourceType, sourceSubtype, undefined, user);
items.push(
- {interactionConditionText(source, condition, conditionIndex > 0)}
+ {interactionConditionText(source, condition, conditionIndex > 0, user)}
@@ -185,7 +185,7 @@ export default function InteractionsOverview(props: Props) {
let condition = conditions[conditionIndex];
let sourceType = source && source.settings.type;
let sourceSubtype = source && source.settings.subtype;
- let describer = describeMeasurement(condition.measurementType, sourceType, sourceSubtype);
+ let describer = describeMeasurement(condition.measurementType, sourceType, sourceSubtype, undefined, user);
condition.value = describer.toStored(value);
updatedDirtyInteractions.set(interactionIndex, true);
}
diff --git a/src/maps/MapBase.tsx b/src/maps/MapBase.tsx
index fa4c9c2..a5cc0c7 100644
--- a/src/maps/MapBase.tsx
+++ b/src/maps/MapBase.tsx
@@ -34,7 +34,6 @@ import { FeatureCollection, Feature } from "geojson";
import DrawController from "./mapControllers/drawController";
//import { Geometry } from "geojson";
import Map, { MapRef, Marker, MarkerDragEvent } from "react-map-gl/mapbox";
-import { getDistanceUnit } from "utils";
import { MapMouseEvent } from "mapbox-gl";
import { makeStyles } from "@mui/styles";
import { Result } from "@mapbox/mapbox-gl-geocoder";
@@ -499,7 +498,7 @@ export default function MapBase(props: Props) {
lineWidth: 5,
origin: pond.DataOrigin.DATA_ORIGIN_ADAPTIVE,
totalDist:
- getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_METERS
+ user.distanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_METERS
? (data.totalDistanceKm * 1000).toFixed(2)
: (data.totalDistanceKm * 3280.8398950131).toFixed(2)
};
diff --git a/src/maps/mapDrawers/FieldDrawer.tsx b/src/maps/mapDrawers/FieldDrawer.tsx
index ae7a794..07adbf9 100644
--- a/src/maps/mapDrawers/FieldDrawer.tsx
+++ b/src/maps/mapDrawers/FieldDrawer.tsx
@@ -14,7 +14,6 @@ import DisplayDrawer from "common/DisplayDrawer";
import HarvestPlanDisplay from "harvestPlan/HarvestPlanDisplay";
import { Field, fieldScope, HarvestPlan, teamScope } from "models";
import React, { useEffect, useState } from "react";
-import TaskViewer from "tasks/TaskViewer";
import Weather from "weather/weather";
import { getThemeType } from "theme";
import GrainDescriber from "grain/GrainDescriber";
@@ -24,6 +23,7 @@ import { useMobile } from "hooks";
import { useGlobalState, useHarvestPlanAPI, useUserAPI } from "providers";
import { makeStyles } from "@mui/styles";
import FieldActions from "field/FieldActions";
+import FieldTaskList from "field/FieldTaskList";
interface TabPanelProps {
children?: React.ReactNode;
@@ -218,7 +218,7 @@ export default function FieldDrawer(props: Props) {
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
-
+
@@ -273,7 +273,7 @@ export default function FieldDrawer(props: Props) {
-
+
);
diff --git a/src/models/Bin.ts b/src/models/Bin.ts
index e720fec..5ad8635 100644
--- a/src/models/Bin.ts
+++ b/src/models/Bin.ts
@@ -1,8 +1,8 @@
import GrainDescriber from "grain/GrainDescriber";
import { cloneDeep } from "lodash";
import { MarkerData } from "maps/mapMarkers/Markers";
+import { User } from "models";
import { pond } from "protobuf-ts/pond";
-import { getGrainUnit } from "utils";
import { stringToMaterialColour } from "utils/strings";
import { or } from "utils/types";
@@ -236,11 +236,11 @@ export class Bin {
return bpt;
}
- public grainInventory(): number {
+ public grainInventory(user: User): number {
let grain = this.bushels()
- if(getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.bushelsPerTonne() > 1){
+ if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.bushelsPerTonne() > 1){
grain = this.bushels() / this.bushelsPerTonne()
- }else if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.bushelsPerTonne() > 1){
+ }else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.bushelsPerTonne() > 1){
grain = this.bushels() / (this.bushelsPerTonne() * 0.907)
}
return Math.round(grain*100)/100
diff --git a/src/models/CO2.ts b/src/models/CO2.ts
index 62e5d73..fcb1ed5 100644
--- a/src/models/CO2.ts
+++ b/src/models/CO2.ts
@@ -3,8 +3,6 @@ import { quack } from "protobuf-ts/pond";
import { or } from "utils/types";
import { clone, cloneDeep } from "lodash";
import { Component } from "models";
-import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
-import { extractGrainCable } from "pbHelpers/ComponentTypes";
export class CO2 {
public settings: pond.ComponentSettings = pond.ComponentSettings.create();
diff --git a/src/models/Contract.ts b/src/models/Contract.ts
index ce55093..81bc001 100644
--- a/src/models/Contract.ts
+++ b/src/models/Contract.ts
@@ -2,8 +2,9 @@ import { Option } from "common/SearchSelect";
import { GrainOptions, ToGrainOption } from "grain";
import GrainDescriber from "grain/GrainDescriber";
import { cloneDeep } from "lodash";
+import { User } from "models";
import { pond } from "protobuf-ts/pond";
-import { getGrainUnit, stringToMaterialColour } from "utils";
+import { stringToMaterialColour } from "utils";
import { or } from "utils/types";
export class Contract {
@@ -14,26 +15,28 @@ export class Contract {
public label: string = "";
private objKey: string = "";
- public static create(pb?: pond.Contract): Contract {
+ public static create(pb?: pond.Contract, user?: User): Contract {
let my = new Contract();
if (pb) {
my.settings = pond.ContractSettings.fromObject(cloneDeep(or(pb.settings, {})));
my.title = pb.name;
my.objKey = pb.key;
- my.unit = my.measurementUnit();
+ my.unit = my.measurementUnit(user);
my.colour = my.commodityColour();
my.label = my.commodityLabel();
}
return my;
}
- private measurementUnit(): string {
+ private measurementUnit(user?: User): string {
if (this.settings.type === pond.ContractType.CONTRACT_TYPE_GRAIN) {
- if(getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.conversionValue() > 1){
- return "mT"
- }
- if(getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.conversionValue() > 1){
- return "t"
+ if( user ){
+ if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.conversionValue() > 1){
+ return "mT"
+ }
+ if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.conversionValue() > 1){
+ return "t"
+ }
}
return "bu";
}
@@ -63,21 +66,22 @@ export class Contract {
return label;
}
- public static clone(other?: Contract): Contract {
+ public static clone(other?: Contract, user?: User): Contract {
if (other) {
return Contract.create(
pond.Contract.fromObject({
title: other.title,
key: other.objKey,
settings: cloneDeep(other.settings)
- })
+ }),
+ user
);
}
- return Contract.create();
+ return Contract.create(undefined, user);
}
- public static any(data: any): Contract {
- return Contract.create(pond.Contract.fromObject(cloneDeep(data)));
+ public static any(data: any, user?: User): Contract {
+ return Contract.create(pond.Contract.fromObject(cloneDeep(data)), user);
}
public key(): string {
@@ -204,28 +208,28 @@ export class Contract {
return this.conversionValue();
}
- public sizeInPreferredUnit(): number {
+ public sizeInPreferredUnit(user: User): number {
let size = this.settings.size;
switch (this.settings.type) {
case pond.ContractType.CONTRACT_TYPE_GRAIN:
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.conversionValue() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.conversionValue() > 1) {
size = size / this.conversionValue();
}
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.conversionValue() > 1){
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.conversionValue() > 1){
size = size / (this.conversionValue() * 0.907); //the conversion for grain will be stored in metric tonnes, this will convert it to US tons
}
}
return Math.round(size * 100) / 100;
}
- public deliveredInPreferredUnit(): number {
+ public deliveredInPreferredUnit(user: User): number {
let del = this.settings.delivered;
switch (this.settings.type) {
case pond.ContractType.CONTRACT_TYPE_GRAIN:
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.conversionValue() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.conversionValue() > 1) {
del = del / this.conversionValue();
}
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.conversionValue() > 1){
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.conversionValue() > 1){
del = del / (this.conversionValue() * 0.907); //the conversion for grain will be stored in metric tonnes, this will convert it to US tons
}
}
@@ -237,14 +241,15 @@ export class Contract {
public static toStoredUnit(
secondaryUnitVal: number,
contractType: pond.ContractType,
- conversionValue: number
+ conversionValue: number,
+ user: User
): number {
let storedUnitVal = secondaryUnitVal;
switch (contractType) {
//use the value and conversion they entered directly, it is safe to assume they are both the same unit (ton vs tonne) so dont need to convert anything
//before converting to bushels
case pond.ContractType.CONTRACT_TYPE_GRAIN:
- if ((getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE || getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON) && conversionValue > 1) {
+ if ((user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE || user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON) && conversionValue > 1) {
storedUnitVal = secondaryUnitVal * conversionValue;
}
}
diff --git a/src/models/UnitMeasurement.ts b/src/models/UnitMeasurement.ts
index d33c17d..9fc3a42 100644
--- a/src/models/UnitMeasurement.ts
+++ b/src/models/UnitMeasurement.ts
@@ -32,7 +32,7 @@ export class UnitMeasurement {
public static create(pb?: pond.UnitMeasurementsForComponent, user?: User): UnitMeasurement {
let my = new UnitMeasurement();
if (pb) {
- let describer = describeMeasurement(pb.type, pb.componentType);
+ let describer = describeMeasurement(pb.type, pb.componentType, undefined, undefined, user);
let values = pb.values;
if (user) {
values = unitConversion(pb.values, pb.type, user);
diff --git a/src/models/user.ts b/src/models/user.ts
index 6c46a31..22bdb26 100644
--- a/src/models/user.ts
+++ b/src/models/user.ts
@@ -101,4 +101,20 @@ export class User {
}
return this.settings.features.includes(flag);
}
+
+ public tempUnit(): pond.TemperatureUnit {
+ return this.settings.temperatureUnit
+ }
+
+ public pressureUnit(): pond.PressureUnit {
+ return this.settings.pressureUnit
+ }
+
+ public distanceUnit(): pond.DistanceUnit {
+ return this.settings.distanceUnit
+ }
+
+ public grainUnit(): pond.GrainUnit {
+ return this.settings.grainUnit
+ }
}
diff --git a/src/navigation/BottomNavigator.tsx b/src/navigation/BottomNavigator.tsx
index b03a153..6395bd4 100644
--- a/src/navigation/BottomNavigator.tsx
+++ b/src/navigation/BottomNavigator.tsx
@@ -13,7 +13,7 @@ import BinsIcon from "products/Bindapt/BinsIcon";
import { useGlobalState } from "providers";
import { useCallback, useEffect, useState } from "react";
import { useNavigate, useLocation } from "react-router-dom";
-import { IsAdaptiveAgriculture, isBXT, IsMiVent, IsAdCon, IsOmniAir, IsMiPCA } from "services/whiteLabel";
+import { IsAdaptiveAgriculture, isBXT, IsMiVent, IsAdCon, IsOmniAir, IsMiPCA, IsIntellifarms } from "services/whiteLabel";
import FieldsIcon from "products/AgIcons/FieldsIcon";
import NexusSTIcon from "products/Construction/NexusSTIcon";
import OmniAirDeviceIcon from "products/AviationIcons/OmniAirDeviceIcon";
@@ -36,7 +36,8 @@ export default function BottomNavigator(props: Props) {
const { isAuthenticated } = useAuth0();
const [{ user }] = useGlobalState();
const [route, setRoute] = useState(sideIsOpen ? "side" : "");
- const isAdaptive = IsAdaptiveAgriculture();
+ const isAg = IsAdaptiveAgriculture();
+ const isIntel = IsIntellifarms();
const isMiVent = IsMiVent();
const isAdCon = IsAdCon();
const isOmni = IsOmniAir();
@@ -45,7 +46,7 @@ export default function BottomNavigator(props: Props) {
const reRoute = useCallback(
(path: string) => {
if (path === "") {
- if (isAdaptive) {
+ if (isAg || isIntel) {
return "bins";
}
if (isMiVent) {
@@ -74,7 +75,7 @@ export default function BottomNavigator(props: Props) {
}
return path;
},
- [isAdaptive, isMiVent]
+ [isAg, isMiVent, isIntel]
);
const autoDetectRoute = useCallback(() => {
@@ -105,10 +106,10 @@ export default function BottomNavigator(props: Props) {
const authenticatedNavigation = () => {
return (
handleRouteChange(newValue)}>
- {isAdaptive && (
+ {isAg || isIntel && (
} value="visualFarm" />
)}
- {isAdaptive && (
+ {isAg || isIntel && (
} value="bins" />
)}
{isAdCon && (
@@ -136,7 +137,7 @@ export default function BottomNavigator(props: Props) {
) : isAdCon ? (
diff --git a/src/navigation/SideNavigator.tsx b/src/navigation/SideNavigator.tsx
index 6bab36c..815f30b 100644
--- a/src/navigation/SideNavigator.tsx
+++ b/src/navigation/SideNavigator.tsx
@@ -27,6 +27,7 @@ import {
IsAdaptiveAgriculture,
// hasTutorialPlaylist,
IsAdCon,
+ IsIntellifarms,
IsMiPCA,
// isBXT,
IsMiVent,
@@ -165,14 +166,15 @@ export default function SideNavigator(props: Props) {
const authenticatedSideMenu = () => {
const isMiVent = IsMiVent();
- const isAg = IsAdaptiveAgriculture()
+ const isAg = IsAdaptiveAgriculture()
+ const isIntel = IsIntellifarms()
const isStreamline = IsStreamline()
const isOmni = IsOmniAir()
const isMiPCA = IsMiPCA()
const isAdCon = IsAdCon()
return (
- {(isAg || isStreamline || user.hasFeature("admin")) && (
+ {(isAg || isIntel || isStreamline || user.hasFeature("admin")) && (
)}
- {(isAg || isStreamline || user.hasFeature("admin")) && (
+ {(isAg || isIntel || isStreamline || user.hasFeature("admin")) && (
)}
- {(isAg || isStreamline || user.hasFeature("admin")) && (
+ {(isAg || isIntel || isStreamline || user.hasFeature("admin")) && (
}
- {(isAg || isStreamline || user.hasFeature("admin")) && (
+ {(isAg || isIntel || isStreamline || user.hasFeature("admin")) && (
}
- {(isAg || isStreamline || user.hasFeature("admin")) && (
+ {(isAg || isIntel || isStreamline || user.hasFeature("admin")) && (
)}
- {(isAg || isStreamline || user.hasFeature("admin")) && (
+ {(isAg || isIntel || isStreamline || user.hasFeature("admin")) && (
}
- {(isAg || isStreamline || user.hasFeature("admin")) &&
+ {(isAg || isIntel || isStreamline || user.hasFeature("admin")) &&
{
@@ -588,7 +592,9 @@ export default function ObjectHeaterCharts(props: Props) {
describer={describeMeasurement(
quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
tempHum.type(),
- tempHum.subType()
+ tempHum.subType(),
+ undefined,
+ user
)}
lineData={ambientTemps}
numLines={1}
diff --git a/src/objects/ObjectDescriber.tsx b/src/objects/ObjectDescriber.tsx
index 643482a..ccd29ac 100644
--- a/src/objects/ObjectDescriber.tsx
+++ b/src/objects/ObjectDescriber.tsx
@@ -2,13 +2,11 @@ import { Column } from "common/ResponsiveTable";
import { Option } from "common/SearchSelect";
import GrainDescriber from "grain/GrainDescriber";
//import { Column } from "material-table";
-import { Bin, BinYard, Field } from "models";
+import { Bin, BinYard, Field, User } from "models";
//import { Gate } from "models/Gate";
import { GrainBag } from "models/GrainBag";
//import { ObjectHeater } from "models/ObjectHeater";
import { pond } from "protobuf-ts/pond";
-import { getDistanceUnit, getTemperatureUnit } from "utils";
-
interface Sort {
order: string; //what to send to the backend list to sort by
numerical: boolean; //whether to use a numerical or text sort
@@ -20,6 +18,8 @@ export interface ObjectExtension {
isTransactionObject: boolean;
//this will define the columns to be used for the object in a material table
tableColumns: Column[];
+ // Optional factory so columns can depend on the active user settings (units, formatting, etc.)
+ getTableColumns?: (user?: User) => Column[];
//this map will match the title of the column to what the backend needs to perform the ordering
tableSort: Map;
}
@@ -32,6 +32,202 @@ const defaultObject: ObjectExtension = {
tableSort: new Map()
};
+const binColumns = (user?: User): Column[] => {
+ const temperatureUnit =
+ user?.tempUnit() ?? pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS;
+
+ return [
+ {
+ title: "Name",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ return ({row.settings.name}
);
+ }
+ },
+ {
+ title: "Grain",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ let grain = row.settings.inventory?.grainType;
+ if (grain) {
+ return GrainDescriber(grain).name;
+ }
+ }
+ },
+ {
+ title: "Bin Height",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ let d = row.settings.specs?.heightCm;
+ if (d) {
+ if (
+ (user?.distanceUnit() ?? pond.DistanceUnit.DISTANCE_UNIT_FEET) ===
+ pond.DistanceUnit.DISTANCE_UNIT_METERS
+ ) {
+ return (d / 100).toFixed(2) + " m";
+ } else {
+ return (d / 30.48).toFixed(2) + " ft";
+ }
+ }
+ }
+ },
+ {
+ title: "Bin Diameter",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ let d = row.settings.specs?.diameterCm;
+ if (d) {
+ if (
+ (user?.distanceUnit() ?? pond.DistanceUnit.DISTANCE_UNIT_FEET) ===
+ pond.DistanceUnit.DISTANCE_UNIT_METERS
+ ) {
+ return (d / 100).toFixed(2) + " m";
+ } else {
+ return (d / 30.48).toFixed(2) + " ft";
+ }
+ }
+ }
+ },
+ {
+ title: "Custom Grain Name",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ return row.settings.inventory?.customTypeName;
+ }
+ },
+ {
+ title: "Grain Variant",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ return row.settings.inventory?.grainSubtype;
+ }
+ },
+ {
+ title: "Grain Bushels",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ return ({row.settings.inventory ? isNaN(row.settings.inventory.grainBushels) ? 0 : row.settings.inventory.grainBushels : 0}
);
+ }
+ },
+ {
+ title: "Grain Capacity",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ return row.settings.specs?.bushelCapacity;
+ }
+ },
+ {
+ title: "High Temp Warning",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ let t = row.settings.highTemp;
+ if (t) {
+ if (temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
+ return (t * 1.8 + 32).toFixed(2) + " °F";
+ }
+ return t.toFixed(2) + " °C";
+ }
+ }
+ },
+ {
+ title: "Low Temp Warning",
+ cellStyle: {padding: "16px"},
+ render: row => {
+ let t = row.settings.lowTemp;
+ if (t) {
+ if (temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
+ return (t * 1.8 + 32).toFixed(2) + " °F";
+ }
+ return t.toFixed(2) + " °C";
+ }
+ }
+ }
+ ] as Column[];
+};
+
+const grainBagColumns = (user?: User): Column[] => {
+ const distanceUnit =
+ user?.distanceUnit() ?? pond.DistanceUnit.DISTANCE_UNIT_FEET;
+ return [
+ {
+ title: "Name",
+ render: row => {
+ if (row.title) {
+ return ({row.title.toString()}
);
+ }
+ }
+ },
+ {
+ title: "Length",
+ render: row => {
+ if (row.settings.length) {
+ if (distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
+ return row.settings.length.toFixed(2) + " m";
+ } else {
+ return (row.settings.length * 3.281).toFixed(2) + " ft";
+ }
+ }
+ }
+ },
+ {
+ title: "Diameter",
+ render: row => {
+ if (row.settings.diameter) {
+ if (distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
+ return row.settings.diameter.toFixed(2) + " m";
+ } else {
+ return (row.settings.diameter * 3.281).toFixed(2) + " ft";
+ }
+ }
+ }
+ },
+ {
+ title: "Grain",
+ render: row => {
+ if (row.settings.supportedGrain) {
+ return GrainDescriber(row.settings.supportedGrain).name;
+ }
+ }
+ },
+ {
+ title: "Custom Grain",
+ render: row => {
+ return row.settings.customGrain;
+ }
+ },
+ {
+ title: "Grain Variant",
+ render: row => {
+ return row.settings.grainSubtype;
+ }
+ },
+ {
+ title: "Capacity",
+ render: row => {
+ return row.settings.bushelCapacity + " bu";
+ }
+ },
+ {
+ title: "Bushels",
+ render: row => {
+ return row.settings.currentBushels + " bu";
+ }
+ },
+ {
+ title: "Fill Date",
+ render: row => {
+ return row.settings.fillDate;
+ }
+ },
+ {
+ title: "Initial Moisture",
+ render: row => {
+ return row.settings.initialMoisture + "%";
+ }
+ }
+ ] as Column[];
+};
+
export const ObjectExtensions: Map = new Map([
[pond.ObjectType.OBJECT_TYPE_UNKNOWN, defaultObject],
[
@@ -50,109 +246,8 @@ export const ObjectExtensions: Map = new Map([
name: "Bin",
inventoryGroup: "grain",
isTransactionObject: true,
- tableColumns: [
- {
- title: "Name",
- cellStyle: {padding: "16px"},
- render: row => {
- return ({row.settings.name}
);
- }
- },
- {
- title: "Grain",
- cellStyle: {padding: "16px"},
- render: row => {
- let grain = row.settings.inventory?.grainType;
- if (grain) {
- return GrainDescriber(grain).name;
- }
- }
- },
- {
- title: "Bin Height",
- cellStyle: {padding: "16px"},
- render: row => {
- let d = row.settings.specs?.heightCm;
- if (d) {
- if (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
- return (d / 100).toFixed(2) + " m";
- } else {
- return (d / 30.48).toFixed(2) + " ft";
- }
- }
- }
- },
- {
- title: "Bin Diameter",
- cellStyle: {padding: "16px"},
- render: row => {
- let d = row.settings.specs?.diameterCm;
- if (d) {
- if (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
- return (d / 100).toFixed(2) + " m";
- } else {
- return (d / 30.48).toFixed(2) + " ft";
- }
- }
- }
- },
- {
- title: "Custom Grain Name",
- cellStyle: {padding: "16px"},
- render: row => {
- return row.settings.inventory?.customTypeName;
- }
- },
- {
- title: "Grain Variant",
- cellStyle: {padding: "16px"},
- render: row => {
- return row.settings.inventory?.grainSubtype;
- }
- },
- {
- title: "Grain Bushels",
- cellStyle: {padding: "16px"},
- render: row => {
- return ({row.settings.inventory ? isNaN(row.settings.inventory.grainBushels) ? 0 : row.settings.inventory.grainBushels : 0}
);
- }
- },
- {
- title: "Grain Capacity",
- cellStyle: {padding: "16px"},
- render: row => {
- return row.settings.specs?.bushelCapacity;
- }
- },
- {
- title: "High Temp Warning",
- cellStyle: {padding: "16px"},
- render: row => {
- let t = row.settings.highTemp;
- if (t) {
- if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
- return (t * 1.8 + 32).toFixed(2) + " °F";
- } else {
- return t.toFixed(2) + " °C";
- }
- }
- }
- },
- {
- title: "Low Temp Warning",
- cellStyle: {padding: "16px"},
- render: row => {
- let t = row.settings.lowTemp;
- if (t) {
- if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
- return (t * 1.8 + 32).toFixed(2) + " °F";
- } else {
- return t.toFixed(2) + " °C";
- }
- }
- }
- }
- ] as Column[],
+ tableColumns: binColumns(),
+ getTableColumns: (user?: User) => binColumns(user),
tableSort: new Map([
["Name", { order: "name", numerical: false }],
["Grain", { order: "inventory.grainType", numerical: false }],
@@ -395,84 +490,8 @@ export const ObjectExtensions: Map = new Map([
name: "Grainbag",
inventoryGroup: "grain",
isTransactionObject: true,
- tableColumns: [
- {
- title: "Name",
- render: row => {
- if (row.title) {
- return ({row.title.toString()}
);
- }
- }
- },
- {
- title: "Length",
- render: row => {
- if (row.settings.length) {
- if (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
- return row.settings.length.toFixed(2) + " m";
- } else {
- return (row.settings.length * 3.281).toFixed(2) + " ft";
- }
- }
- }
- },
- {
- title: "Diameter",
- render: row => {
- if (row.settings.diameter) {
- if (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
- return row.settings.diameter.toFixed(2) + " m";
- } else {
- return (row.settings.diameter * 3.281).toFixed(2) + " ft";
- }
- }
- }
- },
- {
- title: "Grain",
- render: row => {
- if (row.settings.supportedGrain) {
- return GrainDescriber(row.settings.supportedGrain).name;
- }
- }
- },
- {
- title: "Custom Grain",
- render: row => {
- return row.settings.customGrain;
- }
- },
- {
- title: "Grain Variant",
- render: row => {
- return row.settings.grainSubtype;
- }
- },
- {
- title: "Capacity",
- render: row => {
- return row.settings.bushelCapacity + " bu";
- }
- },
- {
- title: "Bushels",
- render: row => {
- return row.settings.currentBushels + " bu";
- }
- },
- {
- title: "Fill Date",
- render: row => {
- return row.settings.fillDate;
- }
- },
- {
- title: "Initial Moisture",
- render: row => {
- return row.settings.initialMoisture + "%";
- }
- }
- ] as Column[],
+ tableColumns: grainBagColumns(),
+ getTableColumns: (user?: User) => grainBagColumns(user),
tableSort: new Map([
["Name", { order: "name", numerical: false }],
["Length", { order: "length", numerical: true }],
@@ -708,8 +727,7 @@ export const ObjectExtensions: Map = new Map([
]);
export default function ObjectDescriber(type: pond.ObjectType): ObjectExtension {
- let describer = ObjectExtensions.get(type);
- return describer ? describer : defaultObject;
+ return ObjectExtensions.get(type) ?? defaultObject;
}
/**
@@ -745,7 +763,8 @@ export function SearchableObjects(): Option[] {
Object.values(pond.ObjectType).forEach(obj => {
if (typeof obj !== "string") {
let ext = ObjectDescriber(obj);
- if (ext.tableColumns.length > 0) {
+ const columns = ext.getTableColumns ? ext.getTableColumns() : ext.tableColumns;
+ if (columns.length > 0) {
options.push({
label: ext.name,
value: obj
diff --git a/src/objects/ObjectTable.tsx b/src/objects/ObjectTable.tsx
index 7a7b677..2ef6c86 100644
--- a/src/objects/ObjectTable.tsx
+++ b/src/objects/ObjectTable.tsx
@@ -1,6 +1,5 @@
import { Box, Button, Grid2 as Grid } from "@mui/material";
// import { getTableIcons } from "common/ResponsiveTable";
-import SearchBar from "common/SearchBar";
import SearchSelect, { Option } from "common/SearchSelect";
// import MaterialTable, { MTableToolbar } from "material-table";
// import { Bin, BinYard, Field } from "models";
@@ -22,9 +21,8 @@ import {
import { useCallback, useEffect, useState } from "react";
import TeamSearch from "teams/TeamSearch";
import BulkBinSettings from "./bulkEditForms/bulkBinSettings";
-import BulkGrainBagSettings from "./bulkEditForms/bulkGrainBagSettings";
import ResponsiveTable from "common/ResponsiveTable";
-import { cloneDeep, indexOf } from "lodash";
+import { cloneDeep } from "lodash";
//import BulkGateSettings from "./bulkEditForms/bulkGateSettings";
interface customButton {
@@ -61,6 +59,9 @@ export default function ObjectTable(props: Props) {
const [selectedUser, setSelectedUser] = useState(as ?? user.id());
const [selectedPageRows, setSelectedPageRows] = useState
- setMenuAnchorEl(event.currentTarget)}>
-
+ */}
+ completeTask(t)}
+ permissions={permissions}
+ task={task}
+ removeTask={deleteTask}
+ refreshCallback={() => {
+ closeCallback(true)
+ }}
+ keys={props.keys}
+ types={props.types}
+ />
@@ -303,7 +291,7 @@ export default function TaskDrawer(props: Props) {
{taskTime()}
{assignees()}
- {taskActions()}
+ {/* {taskActions()} */}
{noteDrawer()}
);
@@ -316,7 +304,9 @@ export default function TaskDrawer(props: Props) {
displayNext={() => {}}
displayPrev={() => {}}
drawerBody={drawerBody()}
- onClose={closeCallback}
+ onClose={() => {
+ closeCallback(false)}
+ }
open={open}
/>
);
diff --git a/src/tasks/TaskList.tsx b/src/tasks/TaskList.tsx
index 6803831..f94f838 100644
--- a/src/tasks/TaskList.tsx
+++ b/src/tasks/TaskList.tsx
@@ -12,7 +12,6 @@ import ButtonGroup from "common/ButtonGroup";
interface Props {
tasks: Task[];
- editTaskMethod: (task: Task) => void;
markComplete: (task: Task) => void;
deleteTask: (task: Task) => void;
openTask: (taskId: string) => void;
@@ -20,18 +19,21 @@ interface Props {
listHeight?: string | number;
label?: string;
dateToView?: Date;
+ keys: string[]
+ types: string[]
}
export default function TaskList(props: Props) {
const {
- editTaskMethod,
markComplete,
deleteTask,
openTask,
reLoad,
listHeight,
label,
- dateToView
+ dateToView,
+ keys,
+ types
} = props;
const [tasks, setTasks] = useState(props.tasks);
const [incomplete, setIncomplete] = useState([]);
@@ -70,64 +72,16 @@ export default function TaskList(props: Props) {
setComplete(complete);
}, [tasks, dateToView]);
- // const StyledToggleButtonGroup = withStyles(theme => ({
- // grouped: {
- // margin: theme.spacing(-0.5),
- // border: "none",
- // padding: theme.spacing(1),
- // "&:not(:first-child):not(:last-child)": {
- // borderRadius: 24,
- // marginRight: theme.spacing(0.5),
- // marginLeft: theme.spacing(0.5)
- // },
- // "&:first-child": {
- // borderRadius: 24,
- // marginLeft: theme.spacing(0.25)
- // },
- // "&:last-child": {
- // borderRadius: 24,
- // marginRight: theme.spacing(0.25)
- // }
- // },
- // root: {
- // backgroundColor: darken(
- // theme.palette.background.paper,
- // getThemeType() === "light" ? 0.05 : 0.25
- // ),
- // borderRadius: 24,
- // content: "border-box"
- // }
- // }))(ToggleButtonGroup);
-
- // const StyledToggle = withStyles({
- // root: {
- // backgroundColor: "transparent",
- // overflow: "visible",
- // content: "content-box",
- // "&$selected": {
- // backgroundColor: "gold",
- // color: "black",
- // borderRadius: 24,
- // fontWeight: "bold"
- // },
- // "&$selected:hover": {
- // backgroundColor: "rgb(255, 255, 0)",
- // color: "black",
- // borderRadius: 24
- // }
- // },
- // selected: {}
- // })(ToggleButton);
-
const incompleteTasks = incomplete.map((task, index) => (
markComplete(task)}
deleteTask={(task: Task) => deleteTask(task)}
reLoad={reLoad}
openTaskPage={(taskId: string) => openTask(taskId)}
+ keys={keys}
+ types={types}
/>
));
@@ -136,11 +90,12 @@ export default function TaskList(props: Props) {
markComplete(task)}
deleteTask={(task: Task) => deleteTask(task)}
reLoad={reLoad}
openTaskPage={(taskId: string) => openTask(taskId)}
+ keys={keys}
+ types={types}
/>
));
@@ -162,20 +117,6 @@ export default function TaskList(props: Props) {
}
]}
/>
- {/*
- setViewing("upcoming")}>
- Upcoming
-
- setViewing("complete")}
- value={"complete"}
- aria-label="complete">
- Complete
-
- */}
{location !== "/tasks" && (
diff --git a/src/tasks/TaskSettings.tsx b/src/tasks/TaskSettings.tsx
index 9f92548..c2b92ee 100644
--- a/src/tasks/TaskSettings.tsx
+++ b/src/tasks/TaskSettings.tsx
@@ -2,10 +2,12 @@ import {
Avatar,
Box,
Button,
+ Checkbox,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
+ FormControlLabel,
Grid2 as Grid,
InputAdornment,
MenuItem,
@@ -15,23 +17,24 @@ import moment from "moment";
import React, { useState } from "react";
import { pond } from "protobuf-ts/pond";
import { useGlobalState, useTaskAPI } from "providers";
-import { useSnackbar, useUserAPI } from "hooks";
+import { usePermissionAPI, useSnackbar, useUserAPI } from "hooks";
import { useEffect } from "react";
-import { Task, teamScope, User } from "models";
+import { Task, teamScope, User, userScope } from "models";
import ColourPicker from "common/ColourPicker";
import ResponsiveDialog from "common/ResponsiveDialog";
interface Props {
open: boolean;
onClose: (reLoad?: boolean) => void;
- markComplete?: boolean;
startDate?: any;
task?: Task;
- objectKey?: string;
+ objectKey?: string; //deprecated, with the new permission structure keys and types will be what determine what a task is connected to
type?: string;
hasCost?: boolean;
costTitle?: string;
secondaryCostTitle?: string;
+ keys?: string[]
+ types?: string[]
}
export default function TaskSettings(props: Props) {
@@ -45,13 +48,15 @@ export default function TaskSettings(props: Props) {
const [worker, setWorker] = useState(user.id());
const [colour, setColour] = useState("");
const taskAPI = useTaskAPI();
+ const userAPI = useUserAPI();
+ const permissionAPI = usePermissionAPI();
const { openSnack } = useSnackbar();
const [cost, setCost] = useState("");
const [secondaryCost, setSecondaryCost] = useState("");
const [seedCost, setSeedCost] = useState("");
const [poundPerAcre, setPoundPerAcre] = useState(0);
- const userAPI = useUserAPI();
const [users, setUsers] = useState([]);
+ const [shareToCreator, setShareToCreator] = useState(true)
useEffect(() => {
if (props.task) {
@@ -104,7 +109,7 @@ export default function TaskSettings(props: Props) {
startTime: startTime,
end: endDate,
endTime: endTime,
- objectKey: props.objectKey ? props.objectKey : user.id(),
+ objectKey: props.objectKey ? props.objectKey : user.id(), //deprecated
description: taskDescription,
worker: worker,
complete: false,
@@ -120,8 +125,24 @@ export default function TaskSettings(props: Props) {
returnTask.settings = taskSettings;
taskAPI
- .addTask(taskSettings, as)
+ .addTask(taskSettings, as, props.keys, props.types)
.then(resp => {
+ console.log(resp)
+ if(shareToCreator && resp.data.task){
+ let parentScope = userScope(user.id())
+ if(as){
+ parentScope = teamScope(as)
+ }
+
+ permissionAPI.shareObjectByKey(
+ {key: resp.data.task, kind: "task"},
+ parentScope.key,
+ parentScope.kind,
+ [pond.Permission.PERMISSION_READ, pond.Permission.PERMISSION_WRITE, pond.Permission.PERMISSION_USERS, pond.Permission.PERMISSION_SHARE],
+ props.keys,
+ props.types
+ )
+ }
props.onClose(true);
})
.catch(err => {
@@ -318,6 +339,18 @@ export default function TaskSettings(props: Props) {
value={endTime}
onChange={e => setEndTime(e.target.value)}
/>
+ {props.keys && props.types && !props.task &&
+ setShareToCreator(e.target.checked)}
+ color="primary"
+ />
+ }
+ label={"Share task with " + (as ? "team" : "user")}
+ />
+ }
Colour
setColour(color)} />
diff --git a/src/tasks/TaskViewer.tsx b/src/tasks/TaskViewer.tsx
index 12e82d7..b283c5f 100644
--- a/src/tasks/TaskViewer.tsx
+++ b/src/tasks/TaskViewer.tsx
@@ -26,10 +26,12 @@ import moment from "moment";
import { makeStyles } from "@mui/styles";
interface ViewProps {
- objectKey?: string; //only used to save it for the object
+ // objectKey?: string; //only used to save it for the object --Deprecated with new permission structure--
+ keys?: string[]//the keys and types are used to give th object permissions to the task, that is how they will be linked now
+ types?: string[]
label?: string;
drawerView?: boolean;
- loadKeys?: string[]; //if you want to load tasks for a specific object(s)
+ // loadKeys?: string[]; //if you want to load tasks for a specific object(s) --Deprecated with new permission structure-- it should use the keys and types now
overlayButton?: boolean;
}
@@ -87,9 +89,16 @@ const useStyles = makeStyles((theme: Theme) => {
});
export default function TaskViewer(props: ViewProps) {
- const { objectKey, label, drawerView, loadKeys, overlayButton } = props;
+ const {
+ //objectKey,
+ label,
+ drawerView,
+ //loadKeys,
+ keys,
+ types,
+ overlayButton
+ } = props;
const [{ user, as }] = useGlobalState();
- //const [{ as }] = useGlobalState();
const taskAPI = useTaskAPI();
const { openSnack } = useSnackbar();
const [tasks, setTasks] = useState>(new Map([]));
@@ -137,42 +146,12 @@ export default function TaskViewer(props: ViewProps) {
}
};
- const setTaskToEdit = (task: Task) => {
- setEditTask(task);
- openDialog();
- };
-
-
- const loadMultitask = useCallback(() => {
- if (!loadKeys) return;
- if (loadKeys.length > 0) {
- let temp = new Map();
- setLoaded(false);
- taskAPI
- .getMultiTasks(loadKeys, as)
- .then(resp => {
- if(resp.data.tasks){
- resp.data.tasks.forEach(task => {
- if (task.settings) {
- temp.set(task.key, Task.any(task));
- }
- });
- }
- setTasks(temp);
- setLoaded(true);
- })
- .catch(err => {
- openSnack("Failed to load");
- });
- }
- }, [loadKeys, openSnack, taskAPI, as]);
-
//loads tasks from the backend database
const loadTasks = useCallback(() => {
if (!user.id()) return;
let temp = new Map();
taskAPI
- .listTasks(50, 0, "asc", "start", undefined, undefined, as, prevMonth, nextMonth)
+ .listTasks(50, 0, "asc", "start", undefined, undefined, as, prevMonth, nextMonth, keys, types)
.then(resp => {
if(resp.data.tasks){
resp.data.tasks.forEach(task => {
@@ -185,20 +164,20 @@ export default function TaskViewer(props: ViewProps) {
setLoaded(true);
})
.catch(err => {
- console.log(err)
openSnack("Failed to load tasks");
});
- }, [taskAPI, as, user, openSnack, nextMonth, prevMonth]);
+ }, [taskAPI, as, user, openSnack, nextMonth, prevMonth, keys, types]);
useEffect(() => {
- if (drawerView) {
- setTasks(new Map());
- setValue(1);
- loadMultitask();
- } else {
- loadTasks();
- }
- }, [loadTasks, loadMultitask, as, drawerView]);
+ // if (drawerView) {
+ // setTasks(new Map());
+ // setValue(1);
+ // loadMultitask();
+ // } else {
+ // loadTasks();
+ // }
+ loadTasks()
+ }, [loadTasks, as, drawerView]);
const markComplete = (task: Task) => {
task.settings.complete = !task.settings.complete;
@@ -314,12 +293,13 @@ export default function TaskViewer(props: ViewProps) {
loadTasks()}
dateToView={drawerView ? undefined : currentDate}
openTask={(taskId: string) => openSelectedTask(taskId)}
+ keys={[]}
+ types={[]}
/>
loadTasks()}
listHeight={"40vh"}
dateToView={currentDate}
openTask={(taskId: string) => openSelectedTask(taskId)}
+ keys={[]}
+ types={[]}
/>
@@ -423,16 +404,11 @@ export default function TaskViewer(props: ViewProps) {
{
- if (r) {
- if (drawerView) {
- loadMultitask();
- } else {
- loadTasks();
- }
- }
+ loadTasks();
setEditTask(undefined);
setNewTaskDialog(false);
}}
@@ -441,10 +417,14 @@ export default function TaskViewer(props: ViewProps) {
setOpenDrawer(false)}
+ closeCallback={() => {
+ setOpenDrawer(false)
+ loadTasks()
+ }}
completeTask={markComplete}
deleteTask={deleteTask}
- editTask={setTaskToEdit}
+ keys={keys ?? []}
+ types={types ?? []}
/>
}
diff --git a/src/tasks/taskActions.tsx b/src/tasks/taskActions.tsx
new file mode 100644
index 0000000..116cdec
--- /dev/null
+++ b/src/tasks/taskActions.tsx
@@ -0,0 +1,237 @@
+import {
+ IconButton,
+ ListItemIcon,
+ ListItemText,
+ Menu,
+ MenuItem,
+ Theme
+} from "@mui/material";
+import { blue } from "@mui/material/colors";
+import MoreIcon from "@mui/icons-material/MoreVert";
+import ObjectTeamsIcon from "@mui/icons-material/SupervisedUserCircle";
+import { pond } from "protobuf-ts/pond";
+import React, { useState } from "react";
+import { isOffline } from "utils/environment";
+import ObjectTeams from "teams/ObjectTeams";
+import { Task, taskScope } from "models";
+import { makeStyles } from "@mui/styles";
+import { Done, Edit } from "@mui/icons-material";
+import DeleteIcon from "@mui/icons-material/Delete";
+import TaskSettings from "./TaskSettings";
+
+const useStyles = makeStyles((theme: Theme) => ({
+ shareIcon: {
+ color: blue["500"],
+ "&:hover": {
+ color: blue["600"]
+ }
+ },
+ removeIcon: {
+ color: "var(--status-alert)"
+ },
+ red: {
+ color: "var(--status-alert)"
+ }
+}));
+
+interface Props {
+ task: Task;
+ permissions: pond.Permission[];
+ keys: string[]
+ types: string[]
+ refreshCallback: () => void;
+ markComplete: (task: Task) => void;
+ removeTask: (task: Task) => void;
+}
+
+interface OpenState {
+ // share: boolean;
+ // users: boolean;
+ teams: boolean;
+ settings: boolean;
+ // removeSelf: boolean;
+ delete: boolean
+}
+
+export default function TaskActions(props: Props) {
+ const classes = useStyles();
+ const { task, permissions, refreshCallback, keys, types } = props;
+ const [anchorEl, setAnchorEl] = React.useState(null);
+ const [openState, setOpenState] = useState({
+ // share: false,
+ // users: false,
+ teams: false,
+ settings: false,
+ // removeSelf: false
+ delete: false
+ });
+
+ const groupMenu = () => {
+ // const canShare = permissions.includes(pond.Permission.PERMISSION_SHARE);
+ const canEdit = permissions.includes(pond.Permission.PERMISSION_WRITE);
+ const canManageUsers = permissions.includes(pond.Permission.PERMISSION_USERS);
+ return (
+
+ );
+ };
+
+ const dialogs = () => {
+ const key = task.key;
+ // const label = task.title();
+ return (
+
+ {/* setOpenState({ ...openState, share: false })}
+ /> */}
+ {/* setOpenState({ ...openState, users: false })}
+ refreshCallback={refreshCallback}
+ /> */}
+ {/* setOpenState({ ...openState, removeSelf: false })}
+ /> */}
+ setOpenState({ ...openState, teams: false })}
+ keys={keys}
+ types={types}
+ />
+ {
+ setOpenState({ ...openState, settings: false})
+ if(r){
+ refreshCallback()
+ }
+ }}
+ />
+
+ );
+ };
+
+ return (
+
+ ) => setAnchorEl(event.currentTarget)}>
+
+
+ {groupMenu()}
+ {dialogs()}
+
+ );
+}
\ No newline at end of file
diff --git a/src/teams/ObjectTeams.tsx b/src/teams/ObjectTeams.tsx
index d327291..2e0c610 100644
--- a/src/teams/ObjectTeams.tsx
+++ b/src/teams/ObjectTeams.tsx
@@ -41,6 +41,8 @@ import { useNavigate } from "react-router-dom";
import { makeStyles } from "@mui/styles";
import { Share, RemoveCircle as RemoveUserIcon } from "@mui/icons-material";
import CancelSubmit from "common/CancelSubmit";
+import { permissionToString } from "pbHelpers/Permission";
+import { PermissionChanges } from "providers/pond/permissionAPI";
const useStyles = makeStyles((theme: Theme) => {
const avatarBG = theme.palette.secondary["700" as keyof PaletteColor];
@@ -100,6 +102,9 @@ interface Props {
userCallback?: (users?: User[] | Team[] | undefined) => void;
dialog?: string;
cardMode?: boolean;
+ keys?: string[];
+ types?: string[];
+ shareLabel?: string; // a custom label to pass in that will replace the share icon
}
export default function ObjectTeams(props: Props) {
@@ -120,7 +125,10 @@ export default function ObjectTeams(props: Props) {
refreshCallback,
userCallback,
dialog,
- cardMode
+ cardMode,
+ keys,
+ types,
+ shareLabel
} = props;
const prevPermissions = usePrevious(permissions);
const prevIsDialogOpen = usePrevious(isDialogOpen);
@@ -146,7 +154,7 @@ export default function ObjectTeams(props: Props) {
const load = useCallback(() => {
setIsLoading(true);
teamAPI
- .listObjectTeams(scope, as)
+ .listObjectTeams(scope, as, keys, types)
.then((response: any) => {
let rTeams: Team[] = [];
or(response.data, { teams: [] }).teams.forEach((user: any) => {
@@ -184,7 +192,21 @@ export default function ObjectTeams(props: Props) {
};
const submit = () => {
- permissionAPI
+ if(keys && types){
+ let changes: PermissionChanges[] = []
+ users.forEach((user: Team) => {
+ changes.push({
+ key: user.id(),
+ permissions: user.permissions.map(permission => permissionToString(permission))
+ });
+ });
+ permissionAPI.updateObjectPermissions(scope.key, scope.kind, "team", changes, keys, types).then(resp => {
+ console.log("no error")
+ }).catch(err => {
+ console.log("error")
+ })
+ }else{
+ permissionAPI
.updatePermissions(scope, users)
.then((_response: any) => {
success("Users were sucessfully updated for " + label);
@@ -197,10 +219,13 @@ export default function ObjectTeams(props: Props) {
})
.catch((err: any) => {
err.response.data.error
- ? warning(err.response.data.error)
- : error("Error occured when updating users for " + label);
+ ? warning(err.response.data.error)
+ : error("Error occured when updating users for " + label);
close();
});
+
+ }
+
};
const changeUserPermissions = (user: pond.ITeam) => (event: any) => {
@@ -299,12 +324,18 @@ export default function ObjectTeams(props: Props) {
{canShare && (
-
-
-
+ {shareLabel ?
+
+ :
+
+
+
+ }
)}
@@ -588,6 +619,8 @@ export default function ObjectTeams(props: Props) {
permissions={permissions}
isDialogOpen={isShareObjectDialogOpen}
closeDialogCallback={closeShareObjectDialog}
+ keys={keys}
+ types={types}
/>
);
diff --git a/src/teams/ShareWithTeam.tsx b/src/teams/ShareWithTeam.tsx
index 070a8c7..ffda3f0 100644
--- a/src/teams/ShareWithTeam.tsx
+++ b/src/teams/ShareWithTeam.tsx
@@ -88,6 +88,8 @@ interface Props {
permissions: pond.Permission[];
isDialogOpen: boolean;
closeDialogCallback: Function;
+ keys?: string[],
+ types?: string[]
}
export default function ShareObject(props: Props) {
@@ -96,7 +98,7 @@ export default function ShareObject(props: Props) {
const classes = useStyles();
const permissionAPI = usePermissionAPI();
const { info, success } = useSnackbar();
- const { scope, label, permissions, isDialogOpen, closeDialogCallback } = props;
+ const { scope, label, permissions, isDialogOpen, closeDialogCallback, keys, types } = props;
const [sharedPermissions, setSharedPermissions] = useState([
pond.Permission.PERMISSION_READ
]);
@@ -109,7 +111,8 @@ export default function ShareObject(props: Props) {
const [teamKey, setTeamKey] = useState("");
const share = () => {
- permissionAPI.shareObjectByKey(scope, teamKey, sharedPermissions).then(resp => {
+
+ permissionAPI.shareObjectByKey(scope, teamKey, "team", sharedPermissions, keys, types).then(resp => {
let shareBins = true;
if (resp && resp.data && resp.data.existing) {
success(label + " was shared with team");
@@ -123,7 +126,7 @@ export default function ShareObject(props: Props) {
resp.data.bins.forEach(bin => {
if (bin.settings) {
let newScope = binScope(bin.settings?.key);
- permissionAPI.shareObjectByKey(newScope, teamKey, sharedPermissions).catch(_err => {
+ permissionAPI.shareObjectByKey(newScope, teamKey, "team", sharedPermissions, keys, types).catch(_err => {
successBins = false;
});
}
@@ -143,7 +146,7 @@ export default function ShareObject(props: Props) {
resp.data.gates.forEach(gate => {
if (gate) {
let newScope = { key: gate.key, kind: "gate" } as Scope;
- permissionAPI.shareObjectByKey(newScope, teamKey, sharedPermissions).catch(_err => {
+ permissionAPI.shareObjectByKey(newScope, teamKey, "team", sharedPermissions, keys, types).catch(_err => {
successGates = false;
});
}
diff --git a/src/transactions/transactionDataDisplay.tsx b/src/transactions/transactionDataDisplay.tsx
index b64db64..3bd6220 100644
--- a/src/transactions/transactionDataDisplay.tsx
+++ b/src/transactions/transactionDataDisplay.tsx
@@ -2,8 +2,8 @@ import { Box, Typography } from "@mui/material";
import GrainDescriber from "grain/GrainDescriber";
import { Transaction } from "models/Transaction";
import { pond } from "protobuf-ts/pond";
+import { useGlobalState } from "providers";
import React from "react";
-import { getGrainUnit } from "utils";
interface Props {
transaction: Transaction;
@@ -11,13 +11,14 @@ interface Props {
export default function TransactionDataDisplay(props: Props) {
const { transaction } = props;
+ const [{ user }] = useGlobalState();
console.log(transaction)
const grainDisplay = (gt: pond.GrainTransaction) => {
- if(getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && gt.bushelsPerTonne > 1){
+ if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && gt.bushelsPerTonne > 1){
return "Weight: " + Math.round(gt.bushels / gt.bushelsPerTonne*100)/100 + " mT"
}
- if(getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && gt.bushelsPerTonne > 1){
+ if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && gt.bushelsPerTonne > 1){
return "Weight: " + Math.round(gt.bushels / (gt.bushelsPerTonne*0.907)*100)/100 + " t"
}
return "Bushels: " + gt.bushels
diff --git a/src/user/ObjectUsers.tsx b/src/user/ObjectUsers.tsx
index e3221c2..981a838 100644
--- a/src/user/ObjectUsers.tsx
+++ b/src/user/ObjectUsers.tsx
@@ -108,6 +108,7 @@ interface Props {
dialog?: string;
cardMode?: boolean;
useImitation?: boolean;
+ shareLabel?: string; // s string to replace the share icon button with a labelled button
}
export default function ObjectUsers(props: Props) {
@@ -127,8 +128,9 @@ export default function ObjectUsers(props: Props) {
refreshCallback,
userCallback,
dialog,
- cardMode
+ cardMode,
//useImitation
+ shareLabel
} = props;
const prevPermissions = usePrevious(permissions);
const prevIsDialogOpen = usePrevious(isDialogOpen);
@@ -367,11 +369,17 @@ export default function ObjectUsers(props: Props) {
{canShare && (
-
+ {shareLabel}
+
+ :
+
-
-
+
+
+ }
)}
diff --git a/src/utils/units.ts b/src/utils/units.ts
index 41b3601..075a9e1 100644
--- a/src/utils/units.ts
+++ b/src/utils/units.ts
@@ -1,5 +1,6 @@
import { pond } from "protobuf-ts/pond";
+//function is deprecated, use User.tempUnit() instead
export function getTemperatureUnit(): pond.TemperatureUnit {
return localStorage.getItem("temperature") === "f"
? pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
@@ -31,6 +32,7 @@ export const fahrenheitToCelsius = (fahrenheit: number) => {
return (fahrenheit - 32) * (5 / 9);
};
+// function is deprecated, use User.pressureUnit() instead
export function getPressureUnit(): pond.PressureUnit {
return localStorage.getItem("pressure") === "kpa"
? pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS
@@ -44,6 +46,7 @@ export function setPressureUnit(unit: pond.PressureUnit) {
);
}
+// function is deprecated, use User.distanceUnit() instead
export function getDistanceUnit(): pond.DistanceUnit {
return localStorage.getItem("distance") === "m"
? pond.DistanceUnit.DISTANCE_UNIT_METERS
@@ -54,6 +57,7 @@ export function setDistanceUnit(unit: pond.DistanceUnit) {
localStorage.setItem("distance", unit === pond.DistanceUnit.DISTANCE_UNIT_METERS ? "m" : "ft");
}
+// function is deprecated, use User.grainUnit() instead
export function getGrainUnit(): pond.GrainUnit {
switch(localStorage.getItem("grainUnit")){
case "mT":
@@ -81,13 +85,11 @@ export function setGrainUnit(unit: pond.GrainUnit) {
default:
localStorage.setItem("grainUnit", "bu")
}
- // localStorage.setItem("grainUnit", unit === pond.GrainUnit.GRAIN_UNIT_WEIGHT ? "mT" : "bu");
}
-export const distanceConversion = (val: number) => {
+export const distanceConversion = (val: number, distanceUnit: pond.DistanceUnit) => {
let converted = val;
-
- if (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
+ if (distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
converted = val / 3.281;
}
return converted;