diff --git a/README.md b/README.md index 74872fd..b7a7555 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,43 @@ Currently, two official plugins are available: - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +### White-labeling + +Steps to add a new white label + +1. Client Instructions: + +- supply assets and colours to be used for the white-label (favicon if necessary) +- determine a sub-domain to use as mask, dashboard is preferred (ex: dashboard.example.com) +- add a `CNAME` record pointing their custom subdomain (ex: dashboard.example.com) to brandxtech.ca +- add a login button on their website point to `/login` of their custom subdomain (ex: dashboard.example.com/login) + + +2. Create a PWA folder in `public` that is named after the client and generate PWA assets using [this](https://realfavicongenerator.net/) website (use the company logo but you might need to use a custom favicon depending on the result) + +- https://maskable.app/ also create a maskable icon for PWA +- create a 512x512 png as well for PWA + +3. Create a white-label branding folder in `src/assets/whitelabels` and create a `darkLogo.png` and `lightLogo.png` from the client's logo +4. In `src/services/whiteLabel.ts`, add a new `Whitelabel` instance with the required fields and add its hostname mapping to the `whitelabels` map (it is very similar to step one but had some constraints when dealing with the `public` folder) +5. If there are any pages specific to the whitelabel be sure to update the side and bottom navigator + + +6. In `dynamic-config.prod.yml`, add a router entry for the new whitelabel +7. In `pond/http/server.go`, add the sub-domain to the `allowedOrigins` +8. In `pond/whitelabel.go`, add an entry for the new white label + +7. Create a new `Auth0` application for the client + - use a image bucket like [postimg](https://postimages.org/) to store their company logo and favicon + - add a whitelabel logo + - allow necessary `origins`, `callback URLs`, and `CORS domains` to the application settings (see the default application as a guideline) + - customize the Universal Login page to handle the new whitelabel + - check if the client ID matches this client's Auth0 application + - change the Universal Login theme using white-label colours and images stored in a bucket (ex: [postimg](https://postimages.org/)) +8. Enable the white-label sub-domain for CORS in the backend + +- in [http.go](https://gitlab.com/brandx/backend/blob/master/pond/http.go), add the white-label sub-domain to the `AllowedOrigins` list + ## Expanding the ESLint configuration If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: diff --git a/package-lock.json b/package-lock.json index a46ad55..795220e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", @@ -8306,10 +8306,12 @@ } }, "node_modules/date-fns-jalali": { - "version": "4.1.0-0", - "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", - "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", - "license": "MIT" + "version": "4.0.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.0.0-0.tgz", + "integrity": "sha512-EczB+gWceuWCRlacE4T+WmdP+BV/IUQpjQW9aBa9DNcXkKuZFv3WBDqeP2Ew+6YFBtPRRcH5U22+C6gcpwgG8A==", + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/dayjs": { "version": "1.11.19", @@ -11965,7 +11967,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#bcf085706791036841f7047a5b37f00c18e54574", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#c9ef7906fd97cda8ef4bd149ec4a796159a7c067", "dependencies": { "protobufjs": "^6.8.8" } @@ -12174,6 +12176,12 @@ "react": ">=16.8.0" } }, + "node_modules/react-day-picker/node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", diff --git a/package.json b/package.json index 7f5044c..90ac71b 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", diff --git a/public/MiPCA/android-chrome-192x192.png b/public/MiPCA/android-chrome-192x192.png new file mode 100644 index 0000000..4ed494c Binary files /dev/null and b/public/MiPCA/android-chrome-192x192.png differ diff --git a/public/MiPCA/android-chrome-512x512.png b/public/MiPCA/android-chrome-512x512.png new file mode 100644 index 0000000..72ba797 Binary files /dev/null and b/public/MiPCA/android-chrome-512x512.png differ diff --git a/public/MiPCA/apple-touch-icon.png b/public/MiPCA/apple-touch-icon.png new file mode 100644 index 0000000..c5d4d8b Binary files /dev/null and b/public/MiPCA/apple-touch-icon.png differ diff --git a/public/MiPCA/browserconfig.xml b/public/MiPCA/browserconfig.xml new file mode 100644 index 0000000..b3930d0 --- /dev/null +++ b/public/MiPCA/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/public/MiPCA/favicon-16x16.png b/public/MiPCA/favicon-16x16.png new file mode 100644 index 0000000..f54fc91 Binary files /dev/null and b/public/MiPCA/favicon-16x16.png differ diff --git a/public/MiPCA/favicon-32x32.png b/public/MiPCA/favicon-32x32.png new file mode 100644 index 0000000..48b5897 Binary files /dev/null and b/public/MiPCA/favicon-32x32.png differ diff --git a/public/MiPCA/favicon.ico b/public/MiPCA/favicon.ico new file mode 100644 index 0000000..48e87f6 Binary files /dev/null and b/public/MiPCA/favicon.ico differ diff --git a/public/MiPCA/manifest.json b/public/MiPCA/manifest.json new file mode 100644 index 0000000..d98df92 --- /dev/null +++ b/public/MiPCA/manifest.json @@ -0,0 +1,33 @@ +{ + "name": "MiPCA", + "short_name": "MiPCA", + "icons": [ + { + "src": "favicon.ico", + "sizes": "48x48", + "type": "image/x-icon" + }, + { + "src": "android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "maskable_icon.png", + "sizes": "196x196", + "type": "image/png", + "purpose": "any maskable" + } + ], + "start_url": "/", + "theme_color": "#272727", + "background_color": "#303030", + "display": "standalone", + "orientation": "any" + } + diff --git a/public/MiPCA/mstile-150x150.png b/public/MiPCA/mstile-150x150.png new file mode 100644 index 0000000..d5b8d98 Binary files /dev/null and b/public/MiPCA/mstile-150x150.png differ diff --git a/public/MiPCA/safari-pinned-tab.svg b/public/MiPCA/safari-pinned-tab.svg new file mode 100644 index 0000000..2758652 --- /dev/null +++ b/public/MiPCA/safari-pinned-tab.svg @@ -0,0 +1,50 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + diff --git a/src/assets/whitelabels/MiPCA/MiPCALogo.png b/src/assets/whitelabels/MiPCA/MiPCALogo.png new file mode 100644 index 0000000..c828035 Binary files /dev/null and b/src/assets/whitelabels/MiPCA/MiPCALogo.png differ diff --git a/src/bin/BinCardV2.tsx b/src/bin/BinCardV2.tsx index 776af57..f39c553 100644 --- a/src/bin/BinCardV2.tsx +++ b/src/bin/BinCardV2.tsx @@ -200,25 +200,27 @@ export default function BinCard(props: Props) { const typeDisplay = () => { let grainType = bin.settings.inventory?.grainType ?? pond.Grain.GRAIN_NONE; - if (bin.storage() === pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN) { - return ( - - - {GrainDescriber(grainType).name} - - - {bin.settings.inventory?.grainSubtype} - - + switch (bin.storage()){ + case pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN: + return ( + + + {GrainDescriber(grainType).name} + + + {bin.settings.inventory?.grainSubtype} + + + ) + default: + return ( + + + {bin.grainName()} + + ); } - return ( - - - {bin.settings.inventory?.customTypeName} - - - ); }; const tempDisplay = () => { @@ -425,6 +427,7 @@ export default function BinCard(props: Props) { hottestNodeTemp={valDisplay === "high" ? hotNode?.temp : undefined} coldestNodeTemp={valDisplay === "low" ? coldNode?.temp : undefined} inventoryControl={bin.inventoryControl()} + customGrain={bin.customGrain()} /> diff --git a/src/bin/BinConditioningCard.tsx b/src/bin/BinConditioningCard.tsx index cf3b3aa..8caac52 100644 --- a/src/bin/BinConditioningCard.tsx +++ b/src/bin/BinConditioningCard.tsx @@ -39,11 +39,12 @@ interface Props { heaters?: Controller[]; fans?: Controller[]; grain?: pond.Grain; + customGrain?: pond.GrainSettings } export default function BinConditioningCard(props: Props) { const classes = useStyles(); - const { interactions, interactionDevices, plenums, ambients, heaters, fans, grain, mode } = props; + const { interactions, interactionDevices, plenums, ambients, heaters, fans, grain, mode, customGrain } = props; const [sourceMap, setSourceMap] = useState>(new Map()); const [sinkMap, setSinkMap] = useState>(new Map()); @@ -87,6 +88,7 @@ export default function BinConditioningCard(props: Props) { sink={sink} source={source} grain={grain} + customGrain={customGrain} /> ); } diff --git a/src/bin/BinConditioningInteraction.tsx b/src/bin/BinConditioningInteraction.tsx index 7fe8bea..bd6c3c7 100644 --- a/src/bin/BinConditioningInteraction.tsx +++ b/src/bin/BinConditioningInteraction.tsx @@ -84,10 +84,11 @@ interface Props { source: Component; sink: Component; grain?: pond.Grain; + customGrain?: pond.GrainSettings } export default function BinConditioningInteraction(props: Props) { - const { interaction, source, sink, grain, deviceId } = props; + const { interaction, source, sink, grain, deviceId, customGrain } = props; const [sliderVals, setSliderVals] = useState>(new Map()); const [sliderMarks, setSliderMarks] = useState>(new Map()); //this is the emc value calculated from the interactions temp and humidity conditions @@ -127,7 +128,6 @@ export default function BinConditioningInteraction(props: Props) { if ( grain !== undefined && grain !== pond.Grain.GRAIN_INVALID && - grain !== pond.Grain.GRAIN_CUSTOM && temp && hum ) { @@ -135,8 +135,9 @@ export default function BinConditioningInteraction(props: Props) { //the emc calc needs the temp to be in celsius temp = fahrenheitToCelsius(temp); } - let emc = ExtractMoisture(grain, temp, hum); - setBaseEMC(emc); + let emc = ExtractMoisture(grain, temp, hum, customGrain) + setBaseEMC(emc === hum ? undefined : emc); + } }, [sliderVals, grain]); diff --git a/src/bin/BinSVGV2.tsx b/src/bin/BinSVGV2.tsx index f3c3454..ecbeb53 100644 --- a/src/bin/BinSVGV2.tsx +++ b/src/bin/BinSVGV2.tsx @@ -147,6 +147,7 @@ interface Props { hottestNodeTemp?: number; coldestNodeTemp?: number; inventoryControl?: pond.BinInventoryControl; + customGrain?: pond.GrainSettings; } interface GrainNodePoint { @@ -211,7 +212,8 @@ export default function BinSVGV2(props: Props) { hottestNodeTemp, coldestNodeTemp, inventoryControl, - co2Sensors + co2Sensors, + customGrain } = props; const [{ user }] = useGlobalState(); const [extraDetails, setExtraDetails] = useState<"temps" | "hums">("temps"); @@ -683,7 +685,7 @@ export default function BinSVGV2(props: Props) { x: cablePos, y: nodeY + 10, stroke: "green", - value: ExtractMoisture(props.grainType, temp, cable.humidities[index] ?? 0).toFixed(1)+"%", + value: ExtractMoisture(props.grainType, temp, cable.humidities[index] ?? 0, customGrain).toFixed(1)+"%", }) !showTempHum && nodeClickData.push({ diff --git a/src/bin/BinSettings.tsx b/src/bin/BinSettings.tsx index 61389f9..4192974 100644 --- a/src/bin/BinSettings.tsx +++ b/src/bin/BinSettings.tsx @@ -65,6 +65,7 @@ import { getDistanceUnit, or, getTemperatureUnit, getGrainUnit } from "utils"; import { makeStyles } from "@mui/styles"; import { useNavigate } from "react-router-dom"; import BinSelector from "./BinSelector"; +import CustomGrainSelector from "grain/CustomGrainSelector"; // import BinSelector from "./BinSelector"; const useStyles = makeStyles((theme: Theme) => { @@ -205,6 +206,7 @@ export default function BinSettings(props: Props) { const libracartAPI = useLibraCartProxyAPI() const [lcDestination, setlcDestination] = useState