diff --git a/package-lock.json b/package-lock.json index a4e6440..5de6592 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "react-color": "^2.19.3", "react-day-picker": "^9.6.5", "react-dom": "^18.3.1", + "react-emoji-render": "^2.0.1", "react-error-boundary": "^5.0.0", "react-full-screen": "^1.1.1", "react-horizontal-scrolling-menu": "^7.1.1", @@ -9188,6 +9189,15 @@ "node": ">=12" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -9971,6 +9981,18 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -11107,6 +11129,29 @@ "react": "^18.3.1" } }, + "node_modules/react-emoji-render": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-emoji-render/-/react-emoji-render-2.0.1.tgz", + "integrity": "sha512-SKtsdwgEf2BFNiE9y4UBFZBWjkRcyWmhMprVly52+J77/zxThcfaQ3sCA0+2LtGJIRMpm4DGWSvyLg72fd1rXQ==", + "license": "MIT", + "dependencies": { + "classnames": "^2.2.5", + "emoji-regex": "^8.0.0", + "lodash.flatten": "^4.4.0", + "prop-types": "^15.5.8", + "string-replace-to-array": "^1.0.1" + }, + "peerDependencies": { + "react": ">=0.14.0", + "react-dom": ">=0.14.0" + } + }, + "node_modules/react-emoji-render/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/react-error-boundary": { "version": "5.0.0", "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", @@ -12408,6 +12453,17 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-replace-to-array": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string-replace-to-array/-/string-replace-to-array-1.0.3.tgz", + "integrity": "sha512-QIdKvmfXbdFvblXAAz6IIjR7A+C6SU6m2A+e7fE/0EYDC5yfeWNMJQ193fPsW7nG+9q52dv/UjnVrDaNVZXpmQ==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.1", + "lodash.flatten": "^4.2.0", + "lodash.isstring": "^4.0.1" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", diff --git a/package.json b/package.json index afa0dea..7081990 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "react-color": "^2.19.3", "react-day-picker": "^9.6.5", "react-dom": "^18.3.1", + "react-emoji-render": "^2.0.1", "react-error-boundary": "^5.0.0", "react-full-screen": "^1.1.1", "react-horizontal-scrolling-menu": "^7.1.1", diff --git a/public/Streamline/stream-logo-dark.png b/public/Streamline/stream-logo-dark.png new file mode 100644 index 0000000..a486c1e Binary files /dev/null and b/public/Streamline/stream-logo-dark.png differ diff --git a/src/app/main.tsx b/src/app/main.tsx index e2acdd9..2d8615c 100644 --- a/src/app/main.tsx +++ b/src/app/main.tsx @@ -19,7 +19,7 @@ if ('serviceWorker' in navigator) { } createRoot(document.getElementById('root')!).render( - + // - , + // , ) diff --git a/src/bin/BinsList.tsx b/src/bin/BinsList.tsx index 6779258..af14b88 100644 --- a/src/bin/BinsList.tsx +++ b/src/bin/BinsList.tsx @@ -123,7 +123,9 @@ export default function BinsList(props: Props) { }} key={i} className={classes.gridListTile} - onClick={() => goToBin(i)}> + onClick={() => { + !duplicate && goToBin(i) + }}> goToBin(i)}> + onClick={() => { + !duplicate && goToBin(i) + }}> { return ({ @@ -46,6 +47,10 @@ const useStyles = makeStyles((theme: Theme) => { }) }); +export interface TourStep extends Step { + onNext?: () => void; // Define onNext as an optional function +} + const StepContainer = (props: TooltipRenderProps, numSteps: number, skip: () => void) => { const { continuous, @@ -114,18 +119,30 @@ const StepContainer = (props: TooltipRenderProps, numSteps: number, skip: () => interface Props { run: boolean; - steps: Step[]; + steps: TourStep[]; endTourCallback: () => void; + spotlightStyle?: CSSProperties, + setStepIndex?: React.Dispatch>, } export default function Tour(props: Props) { - const { run, steps, endTourCallback } = props; + const { run, steps, endTourCallback, setStepIndex, spotlightStyle } = props; const theme = useTheme(); const handleTourCallback = (data: CallBackProps) => { - const { status } = data; + const { action, index, status, type } = data; const finishedStatuses: string[] = [STATUS.FINISHED, STATUS.SKIPPED]; + if (setStepIndex) setStepIndex(index) + + if (action === ACTIONS.NEXT && type === EVENTS.STEP_AFTER) { + // Safely access the step and call onNext if it exists + const currentStep = steps[index]; + if (currentStep.onNext) { + currentStep.onNext(); + } + } + if (finishedStatuses.includes(status)) { endTourCallback(); } @@ -150,6 +167,9 @@ export default function Tour(props: Props) { primaryColor: theme.palette.primary.main, textColor: theme.palette.text.primary, zIndex: theme.zIndex.modal + }, + spotlight: { + ...spotlightStyle } }} /> diff --git a/src/device/SyncDevice.tsx b/src/device/SyncDevice.tsx index e816967..f608d28 100644 --- a/src/device/SyncDevice.tsx +++ b/src/device/SyncDevice.tsx @@ -50,7 +50,10 @@ export default function SyncDevice(props: Props) { console.error(err); error("Unable to resync device"); close(); - }); + }) + .finally(() => { + close() + }) }; return ( diff --git a/src/grain/GrainDescriber.ts b/src/grain/GrainDescriber.ts index 00f9bb0..ce394b3 100644 --- a/src/grain/GrainDescriber.ts +++ b/src/grain/GrainDescriber.ts @@ -475,6 +475,37 @@ export const GrainExtensions: Map = new Map([ weightConversionKg: 27.2158214642112, bushelsPerTonne: 36.744 } + ], + [ + pond.Grain.GRAIN_DRY_BEANS_RED, + { + name: "Dry Beans Red", + group: "Dry Beans", + equation: Equation.halsey, + a: 4.2669, + b: -0.013382, + c: 1.6933, + setTempC: defaultSetTemp, + targetMC: 15.0, + colour: "red", + weightConversionKg: 27.2155, + bushelsPerTonne: 36.744 + } + ],[ + pond.Grain.GRAIN_DRY_BEANS_BLACK, + { + name: "Dry Beans Black", + group: "Dry Beans", + equation: Equation.halsey, + a: 5.2003, + b: -0.022685, + c: 1.9656, + setTempC: defaultSetTemp, + targetMC: 15.0, + colour: "grey", + weightConversionKg: 27.2155, + bushelsPerTonne: 36.744 + } ] ]); diff --git a/src/models/Device.ts b/src/models/Device.ts index 6311981..e45f7aa 100644 --- a/src/models/Device.ts +++ b/src/models/Device.ts @@ -36,17 +36,17 @@ const featureVersions: Map = new Map([ ],[ "detectI2C", { - photon: "NA", - electron: "NA", - v2Wifi: "2.1.6", - v2Cell: "2.1.6", - v2WifiS3: "2.1.6", - v2CellS3: "2.1.6", - v2CellBlack: "2.1.6", - v2CellGreen: "2.1.6", - v2WifiBlue: "2.1.6", - v2CellBlue: "2.1.6", - v2EthBlue: "2.1.6" + photon: "N/A", + electron: "N/A", + v2Wifi: "N/A", + v2Cell: "N/A", + v2WifiS3: "N/A", + v2CellS3: "N/A", + v2CellBlack: "N/A", + v2CellGreen: "N/A", + v2WifiBlue: "N/A", + v2CellBlue: "N/A", + v2EthBlue: "N/A" } ] ]); diff --git a/src/navigation/Router.tsx b/src/navigation/Router.tsx index 29362ad..f6badb1 100644 --- a/src/navigation/Router.tsx +++ b/src/navigation/Router.tsx @@ -9,6 +9,7 @@ import { getWhitelabel } from "services/whiteLabel"; import Ventilation from "pages/VentEditor"; import { useGlobalState } from "providers"; import DeviceSupport from "pages/DeviceSupport"; +import SignupCallback from "pages/SignupCallback"; const DeviceHistory = lazy(() => import("pages/DeviceHistory")); const DevicePage = lazy(() => import("pages/Device")); @@ -302,6 +303,8 @@ export default function Router() { {/* Redirects */} } /> + {/* } /> */} + } /> {/* } /> */} {/* } /> */} {/* } /> */} diff --git a/src/navigation/SideNavigator.tsx b/src/navigation/SideNavigator.tsx index 38a10cb..f71ca9c 100644 --- a/src/navigation/SideNavigator.tsx +++ b/src/navigation/SideNavigator.tsx @@ -221,7 +221,7 @@ export default function SideNavigator(props: Props) { {(isAg || isStreamline || user.hasFeature("admin")) && ( goTo("/bins")} classes={getClasses("/bin")} > @@ -277,7 +277,7 @@ export default function SideNavigator(props: Props) { {(isMiVent || user.hasFeature("admin")) && ( goTo("/mines")} classes={getClasses("/mine")} > @@ -330,7 +330,7 @@ export default function SideNavigator(props: Props) { } goTo("/teams")} classes={getClasses("/team")} > @@ -342,7 +342,7 @@ export default function SideNavigator(props: Props) { goTo("/users")} classes={getClasses("/user")} > diff --git a/src/pages/Bin.tsx b/src/pages/Bin.tsx index fc9463f..fcc1117 100644 --- a/src/pages/Bin.tsx +++ b/src/pages/Bin.tsx @@ -1107,7 +1107,7 @@ export default function Bin(props: Props) { }; return ( - + {showPlenumError()} { @@ -141,11 +140,13 @@ export default function Bins(props: Props) { const classes = useStyles(); const isMobile = useMobile(); const binAPI = useBinAPI(); + const teamAPI = useTeamAPI(); // const binyardAPI = useBinYardAPI(); const interactionsAPI = useInteractionsAPI(); const [{ user, as }] = useGlobalState(); const [allLoading, setAllLoading] = useState(false); const [binsLoading, setBinsLoading] = useState(false); + const [doneLoadingPage, setDoneLoadingPage] = useState(false) const [addBinOpen, setAddBinOpen] = useState(false); const [paginatedBins, setPaginatedBins] = useState({ bins: [], @@ -188,13 +189,16 @@ export default function Bins(props: Props) { const [qrKeyList, setQrKeyList] = useState([]); const [totalFanControllers, setTotalFanControllers] = useState(0); const [totalFanControllersOn, setTotalFanControllersOn] = useState(0); - const [yardsLoading, setYardsLoading] = useState(false); + // const [yardsLoading, setYardsLoading] = useState(false); const [openAddAlerts, setOpenAddAlerts] = useState(false); const [alertBins, setAlertBins] = useState([]); const { openSnack } = useSnackbar(); // const history = useHistory(); const navigate = useNavigate() const [cardValDisplay, setCardValDisplay] = useState<"high" | "low" | "average">("average"); + + const [teamsDialog, setTeamsDialog] = useState(false) + // const [scrollTranslations, setScrollTranslations] = useState({ // bins: 0, // empty: 0, @@ -252,6 +256,18 @@ export default function Bins(props: Props) { // selected: {} // })); + useEffect(() => { + if (doneLoadingPage && grainBins.length < 1 && grainBags.length < 1) { + teamAPI.listTeams(1, 0).then(resp => { + // console.log(resp.data.teams) + if (resp.data.teams.length > 0) { + // console.log("should?") + setTeamsDialog(true) + } + }) + } + }, [doneLoadingPage, grainBins, grainBags]) + useEffect(() => { let ebt = sessionStorage.getItem("expandBinTotal"); if (ebt === "true") { @@ -400,7 +416,7 @@ export default function Bins(props: Props) { const bags = resp.data.bags.map(b => GrainBag.create(b)); setGrainBags(bags); }) - .catch(err => { + .catch(() => { setPaginatedBins({ bins: [], binsOffset: 0, binsTotal: 0 }); setBinMetrics(pond.BinMetrics.create()); setYards([]); @@ -408,6 +424,7 @@ export default function Bins(props: Props) { .finally(() => { setAllLoading(false); setBinsLoading(false); + setDoneLoadingPage(true) }); }, [binAPI, yardFilter, order, orderBy, as, loadingAs, loadingFilter]); // eslint-disable-line react-hooks/exhaustive-deps @@ -454,7 +471,7 @@ export default function Bins(props: Props) { setFertilizerBins([...fert]); setQrKeyList([...newKeyList]); }) - .catch(err => { + .catch(() => { setPaginatedBins({ bins: [], binsOffset: 0, binsTotal: 0 }); }) .finally(() => { @@ -462,13 +479,13 @@ export default function Bins(props: Props) { }); }; - const searchYards = ( - search?: string, - limit?: number, - offset?: number, - order?: "asc" | "desc" - ) => { - setYardsLoading(true); + // const searchYards = ( + // search?: string, + // limit?: number, + // offset?: number, + // order?: "asc" | "desc" + // ) => { + // setYardsLoading(true); // binyardAPI.listBinYards(limit ?? binsLimit, offset ?? 0, order, "name", search).then(resp => { // let y: pond.BinYardSettings[] = []; // let p: Dictionary = {}; @@ -493,7 +510,7 @@ export default function Bins(props: Props) { // } // setYardsLoading(false); // }); - }; + // }; useEffect(() => { loadBins(); @@ -515,8 +532,8 @@ export default function Bins(props: Props) { } const duplicateBin = (bin: Bin) => { - binAPI.addBin(bin.settings, as).then(resp => { - loadBins(); + binAPI.addBin(bin.settings, as).then(() => { + loadMoreBins(contentFilter); }); }; @@ -556,7 +573,7 @@ export default function Bins(props: Props) { .then(resp => { openSnack("Successfully added " + resp.data.numAlertsSet + " alerts"); }) - .catch(err => { + .catch(() => { console.log("errors found"); }); }; @@ -1080,7 +1097,7 @@ export default function Bins(props: Props) { control={ { + onChange={(_, checked) => { setDisplayGrain(checked); }} /> @@ -1093,7 +1110,7 @@ export default function Bins(props: Props) { control={ { + onChange={(_, checked) => { setDisplayFert(checked); }} /> @@ -1106,7 +1123,7 @@ export default function Bins(props: Props) { control={ { + onChange={(_, checked) => { setDisplayEmpty(checked); }} /> @@ -1392,15 +1409,17 @@ export default function Bins(props: Props) { {addALertsDialog()} { - searchYards(search, limit, offset); - }} + // loadYards={(search, limit, offset) => { + // searchYards(search, limit, offset); + // }} + loadYards={() => {}} grainBags={grainBags} setShowing={(val: "all" | "bins" | "bags") => { setShowing(val); @@ -1473,6 +1492,13 @@ export default function Bins(props: Props) { /> {binMenu()} setAddBinOpen(true)} pulse={binsTotal < 1 && !allLoading} /> + ); }; diff --git a/src/pages/DeviceSupport.tsx b/src/pages/DeviceSupport.tsx index a6a19de..6c9a0db 100644 --- a/src/pages/DeviceSupport.tsx +++ b/src/pages/DeviceSupport.tsx @@ -3,7 +3,7 @@ import PageContainer from "./PageContainer"; import { pond } from "protobuf-ts/pond"; import LogsDisplay from "common/LogsDisplay"; import { useEffect, useState } from "react"; -import { useDeviceAPI, useSnackbar } from "hooks"; +import { useDeviceAPI, useMobile, useSnackbar } from "hooks"; import { useLocation, useParams } from "react-router-dom"; import { Component, Device, Interaction } from "models"; import { getContextKeys, getContextTypes } from "pbHelpers/Context"; @@ -43,6 +43,7 @@ export default function DeviceSupport() { const deviceAPI = useDeviceAPI() const snackbar = useSnackbar() const theme = useTheme() + const isMobile = useMobile() const { state } = useLocation(); const [{ user, as }] = useGlobalState() @@ -188,7 +189,7 @@ export default function DeviceSupport() { } return ( - + diff --git a/src/pages/Devices.tsx b/src/pages/Devices.tsx index fe47b3c..7721ec1 100644 --- a/src/pages/Devices.tsx +++ b/src/pages/Devices.tsx @@ -5,7 +5,7 @@ import { makeStyles } from "@mui/styles"; import ResponsiveTable, { Column } from "common/ResponsiveTable"; import ProvisionDevice from "device/ProvisionDevice"; import { pond } from "protobuf-ts/pond"; -import { useDeviceAPI, useGlobalState, useGroupAPI } from "providers"; +import { useDeviceAPI, useGlobalState, useGroupAPI, useTeamAPI } from "providers"; import { useCallback, useEffect, useState } from "react"; import PageContainer from "./PageContainer"; import { useMobile } from "hooks"; @@ -28,6 +28,7 @@ import StatusDust from "common/StatusDust"; import StatusGas from "common/StatusGas"; import { cloneDeep } from "lodash"; import LoadingScreen from "app/LoadingScreen"; +import TeamDialog from "teams/TeamDialog"; const useStyles = makeStyles((theme: Theme) => { return ({ @@ -98,9 +99,12 @@ export default function Devices() { const location = useLocation(); const deviceAPI = useDeviceAPI(); const groupAPI = useGroupAPI(); + const teamAPI = useTeamAPI(); const [devicesLoading, setDevicesLoading] = useState(false) const [limit, setLimit] = useState(10); const [page, setPage] = useState(0); + const [doneLoading, setDoneLoading] = useState(false) + const [teamsDialog, setTeamsDialog] = useState(false) const [{ user }] = useGlobalState() @@ -186,7 +190,7 @@ export default function Devices() { const [preferencesAnchor, setPreferencesAnchor] = useState(null) - const [{ as }] = useGlobalState() + const [{ as, team }] = useGlobalState() const updateGroups = (newGroups: Group[]) => { setGroups(newGroups); @@ -327,6 +331,7 @@ export default function Devices() { setTotal(resp.data.total) }).finally(() => { setDevicesLoading(false) + setDoneLoading(true) }) } @@ -338,6 +343,16 @@ export default function Devices() { loadGroups() }, [groupLimit, groupPage, orderGroup, orderGroupBy, searchGroup, as]) + useEffect(() => { + if (doneLoading && devices.length < 1 && groups.length < 1) { + teamAPI.listTeams(1, 0).then(resp => { + if (resp.data.teams.length > 0 && team.key().length < 1) { + setTeamsDialog(true); + } + }) + } + }, [doneLoading, devices, groups]) + const handleChange = (event: any) => { setLimit(event.target.value); }; @@ -826,6 +841,13 @@ export default function Devices() { addGroupCallback={addGroupCallback} /> {preferencesMenu()} + ) } \ No newline at end of file diff --git a/src/pages/Firmware.tsx b/src/pages/Firmware.tsx index b3a7c58..1ae97af 100644 --- a/src/pages/Firmware.tsx +++ b/src/pages/Firmware.tsx @@ -12,7 +12,7 @@ import { TypeBackground } from "@mui/material"; import { blue, green, orange, red } from "@mui/material/colors"; -import { createStyles, makeStyles } from "@mui/styles"; +import { makeStyles } from "@mui/styles"; //import { TypeBackground } from "@material-ui/core/styles/createPalette"; import { ArrowUpward, @@ -267,7 +267,7 @@ export default function Firmware() { if (selected) { firmwareAPI .removeFirmware(selected.settings.platform, selected.settings.version) - .then((response: any) => { + .then(() => { success("Successfully removed firmware"); }) .catch((err: any) => { diff --git a/src/pages/PageContainer.tsx b/src/pages/PageContainer.tsx index 524fbbf..2915e5b 100644 --- a/src/pages/PageContainer.tsx +++ b/src/pages/PageContainer.tsx @@ -48,7 +48,7 @@ export const PageContainer: React.FunctionComponent = (props: Props) => { const { children, fullViewport, isCenterCenter, sx } = props; // let fullViewport = false // let isCenterCenter = false - const spacing = props.spacing ? props.spacing : 2; + const spacing = props.spacing ?? 0; return ( { + setLoading(true) + userAPI.completeSignup().then(() => { + teamAPI.listTeams(1, 0).then(resp => { + setHasTeams(resp.data.teams.length > 0) + }).finally(() => setDoneTeams(true)) + deviceAPI.list(1, 0).then(resp => { + setHasDevices(resp.data.devices.length > 0) + }).finally(() => setDoneDevices(true)) + binAPI.listBins(1, 0).then(resp => { + setHasBins(resp.data.bins.length > 0) + }).finally(() => setDoneBins(true)) + }).finally(() => setLoading(false)) + }, []) + + const loadingComplete = () => { + return loading === false && doneBins && doneTeams && doneDevices + } + + const content = () => { + return ( + + { loadingComplete() ? + + : + + } + + {hasTeams && "Teams found!"} +
+ {hasDevices && "Devices found!"} +
+ {hasBins && "Bins found!"} +
+
+ ) + } + + const getTourSteps = (): TourStep[] => { + let steps: TourStep[] = [ + { + title: ( + <> + Welcome to {whiteLabel.name} + + + ), + content: ( + <> + + {"Hello " + user.name()} + + + + Thank you for signing up! Let me give you the tour + + + + ), + target: "body", + placement: "center", + disableBeacon: true, + }, + { + title: "User Menu", + content: ( + <> + + In the user menu, you can make changes to your profile, and adjust unit preferences. + +
+ + If you are part of a team, you can select it here as well. + + + ), + target: "#tour-user-menu", + placement: "bottom", + disableBeacon: true, + // onNext: () => setDoneFirst(true) + }, + // { + // title: + // } + ]; + if (hasTeams) steps.push({ + title: "Teams", + content: ( + <> + + Here, you can view your team(s), access their settings, and choose your favourite. + + + ), + target: "#tour-teams", + placement: "right", + // spotlightPadding: 50, + }) + steps.push({ + title: "Devices", + content: ( + <> + + You can view your devices and their readings here. + +
+ + If you are viewing as a team, your team's devices will be displayed here instead. + + + ), + target: "#tour-dashboard", + placement: "right" + }) + if (IsAdaptiveAgriculture()) steps.push({ + title: "Bins", + content: ( + <> + + You can view your bins and their inventory here. + +
+ + If you are viewing as a team, your team's bins will be displayed here instead. + + + ), + target: "#tour-bins", + placement: "right", + disableBeacon: true, + }) + return steps; + }; + + const endTour = () => { + setIsTourRunning(false); + // if (user) { + // user.status.finishedBinIntro = moment().toJSON(); + // userAPI + // .updateUser(userID, user.protobuf()) + // .then(() => dispatch({ key: "user", value: user })) + // .catch((err: any) => error(err)); + // } + }; + + const closeDialog = () => { + setSignupDialogOpen(false) + setIsTourRunning(true) + } + + useEffect(() => { + console.log(stepIndex) + }, [stepIndex]) + + return ( + + + + Finishing Signup + + + { content() } + + {loadingComplete() && + + } + + + + + ) +} \ No newline at end of file diff --git a/src/pages/Team.tsx b/src/pages/Team.tsx index 7a8f29f..dd88fdd 100644 --- a/src/pages/Team.tsx +++ b/src/pages/Team.tsx @@ -214,7 +214,7 @@ export default function TeamPage() { } return ( - + {title()} diff --git a/src/pages/Teams.tsx b/src/pages/Teams.tsx index 2e74628..90e3401 100644 --- a/src/pages/Teams.tsx +++ b/src/pages/Teams.tsx @@ -5,7 +5,7 @@ import TeamList from "teams/TeamList"; export default function Teams() { const isMobile = useMobile() return ( - + ); diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index 00ef9f7..296c60a 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -349,7 +349,7 @@ export default function Users() { } return ( - + title="Users" rows={rows} diff --git a/src/providers/auth.tsx b/src/providers/auth.tsx index 22ac76d..bc116ff 100644 --- a/src/providers/auth.tsx +++ b/src/providers/auth.tsx @@ -9,28 +9,37 @@ interface Props extends PropsWithChildren{ export default function AuthWrapper(props: Props) { const { children, setToken } = props; - const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently } = useAuth0(); + const { + isLoading, + loginWithRedirect, + isAuthenticated, + getAccessTokenSilently, + // getIdTokenClaims + } = useAuth0(); useEffect(() => { const url = new URL(window.location.href); - const isManualLoginPage = url.pathname.includes("/login"); + const options: RedirectLoginOptions = { + authorizationParams: {} // Initialize here + }; - if (isManualLoginPage) { - const options: RedirectLoginOptions = { - authorizationParams: {} // Initialize here - }; - + if (isManualLoginPage && options.authorizationParams) { + let parsed = queryString.parse(url.search); if (parsed.signup === "true") { - options.authorizationParams!.screen_hint = "signup"; - options.authorizationParams!.prompt = "login"; + options.authorizationParams.screen_hint = "signUp"; + // options.authorizationParams.m + // options.authorizationParams.prompt = "login"; } if (parsed.email && parsed.email !== "") { - options.authorizationParams!.login_hint = parsed.email.toString(); // prefill email + options.authorizationParams.login_hint = parsed.email.toString(); // prefill email } + + options.authorizationParams.redirect_uri = "https://" + url.hostname + "/signupCallback" + console.log(options.authorizationParams.redirect_uri) loginWithRedirect(options) @@ -40,15 +49,27 @@ export default function AuthWrapper(props: Props) { }, [isAuthenticated, loginWithRedirect, isLoading]); useEffect(() => { - if (isAuthenticated) getAccessTokenSilently().then(t => { - if (t.length < 1) { + // const url = new URL(window.location.href); + // const isSignupCallback = url.pathname.includes("/signupCallback"); + if (isAuthenticated) { + getAccessTokenSilently().then(t => { + if (t.length < 1) { + loginWithRedirect() + } + setToken(t) + }).catch(() => { + // No token, go back to login loginWithRedirect() - } - setToken(t) - }).catch(() => { - // No token, go back to login - loginWithRedirect() - }) + }) + // if (isSignupCallback) { + // getIdTokenClaims().then(id_claims => { + // console.log(id_claims) + // console.log(id_claims?.nickname) + // if (id_claims) console.log(Object.keys(id_claims)) + // }) + // } + + } }, [setToken, isAuthenticated]) return children; diff --git a/src/providers/pond/userAPI.tsx b/src/providers/pond/userAPI.tsx index 247f77c..0d33b2e 100644 --- a/src/providers/pond/userAPI.tsx +++ b/src/providers/pond/userAPI.tsx @@ -28,6 +28,7 @@ export interface IUserAPIContext { prefixSearch?: string, ) => Promise; listProfiles: () => Promise; + completeSignup: () => Promise; listObjectUsers: (scope: Scope) => Promise; updateObjectUsers: (scope: Scope, users: pond.IUser[]) => Promise; getUser: (id: string, scope?: Scope) => Promise; @@ -55,8 +56,21 @@ export const UserAPIContext = createContext({} as IUserAPIConte export default function UserProvider(props: PropsWithChildren) { const { children } = props; - const { get, put } = useHTTP(); - const [{ as }] = useGlobalState(); + const { get, put, post } = useHTTP(); + const [{ as, user }] = useGlobalState(); + + const completeSignup = (): Promise => { + return new Promise((resolve, reject) => { + let request = pond.CompleteSignupUserRequest.create(); + request.avatar = user.settings.avatar + request.email = user.settings.email + request.id = user.id() + request.name = user.name() + post(pondURL("/signups"), request).then(res => { + resolve(pond.CompleteSignupUserResponse.create()) + }) + }) + } const listUsers = ( limit: number, @@ -234,6 +248,7 @@ export default function UserProvider(props: PropsWithChildren) { return ( ) => void; + title?: string; + info?: string; + hideKey?: string; } const useStyles = makeStyles((theme: Theme) => { @@ -37,16 +43,22 @@ const useStyles = makeStyles((theme: Theme) => { }); export default function TeamDialog(props: Props) { - const { open, setOpen } = props; + const { open, setOpen, title, info, hideKey } = props; const teamAPI = useTeamAPI(); const userAPI = useUserAPI(); const [, dispatch] = useGlobalState(); const [teamKey, setTeamKey] = useState(""); const [{ team, user }] = useGlobalState(); + const [viewAsTeam, setViewAsTeam] = useState(user.settings.useTeam) + const [doNotShow, setDoNotShow] = useState(false) const classes = useStyles(); + const shouldHide = hideKey ? + (localStorage.getItem(hideKey) === "true" ? true : false) + : false + const setTeam = () => { - setOpen(false); + onClose() dispatch({ key: "as", value: teamKey }); if (teamKey === "" || teamKey.includes("auth")) { dispatch({ key: "team", value: Team.create() }); @@ -55,13 +67,19 @@ export default function TeamDialog(props: Props) { teamAPI.getTeam(teamKey).then(team => { dispatch({ key: "team", value: team }); user.settings.defaultTeam = team.key(); - user.settings.useTeam = true; //when selecting a team now set to view as team by default + user.settings.useTeam = viewAsTeam; //when selecting a team now set to view as team by default userAPI.updateUser(user.id(), user.protobuf()); }); }; + const onClose = () => { + console.log("close") + setOpen(false) + if (hideKey) localStorage.setItem(hideKey, ""+doNotShow) + } + return ( - setOpen(false)}> + - Select a Team to View As + { title ? title : "Select a Team to View As"} - @@ -84,12 +102,40 @@ export default function TeamDialog(props: Props) { Viewing as: {team.name()} )} + {info && + + {info} + + } + {/* setViewAsTeam(!viewAsTeam)} /> */} + + setViewAsTeam(!viewAsTeam)} + /> + } + label="View as team" + /> + + {hideKey && + setDoNotShow(!doNotShow)} + /> + } + label="Do not show again" + /> + } ); } diff --git a/src/user/UserMenu.tsx b/src/user/UserMenu.tsx index 2b41a6f..c7b8ab3 100644 --- a/src/user/UserMenu.tsx +++ b/src/user/UserMenu.tsx @@ -228,7 +228,7 @@ export default function UserMenu() { */} {hasTeams && ( - +