diff --git a/.github/workflows/cd-beta.yaml b/.github/workflows/cd-beta.yaml
index 4985cced7a..c88cb8940a 100644
--- a/.github/workflows/cd-beta.yaml
+++ b/.github/workflows/cd-beta.yaml
@@ -57,7 +57,7 @@ jobs:
# SLACK_COLOR: "#FF0000"
# SLACK_ICON: https://clipart.world/wp-content/uploads/2021/06/Rocket-Ship-clipart-png.png
# SLACK_MESSAGE: "PR merge by ${{ github.actor }} failed to deploy."
- # SLACK_TITLE: FAILED Beta Deployment for manager-ui
+ # SLACK_TITLE: FAILED: Beta Deployment for manager-ui
# SLACK_USERNAME: Deploy Bot
# SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
@@ -73,7 +73,7 @@ jobs:
# SLACK_COLOR: "#FF0000"
# SLACK_ICON: https://clipart.world/wp-content/uploads/2021/06/Rocket-Ship-clipart-png.png
# SLACK_MESSAGE: "PR merge by ${{ github.actor }} has been deployed to stage."
- # SLACK_TITLE: Beta Deployment for manager-ui
+ # SLACK_TITLE: BETA:manager-ui
# SLACK_USERNAME: Deploy Bot
# SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
diff --git a/.github/workflows/cd-dev.yaml b/.github/workflows/cd-dev.yaml
index e03c3e86b1..1750b4374c 100644
--- a/.github/workflows/cd-dev.yaml
+++ b/.github/workflows/cd-dev.yaml
@@ -72,11 +72,11 @@ jobs:
SLACK_COLOR: "#75BF43"
SLACK_ICON: https://brand.zesty.io/zesty-io-logo.svg
# SLACK_MESSAGE: "PR merge by ${{ github.actor }} has been deployed to stage."
- SLACK_TITLE: "SUCCESS: Dev Deployment of manager-ui"
+ SLACK_TITLE: "DEV:manager-ui"
SLACK_USERNAME: Deploy Bot
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
- # create_stage_release:
+ # create_release:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout Repo
diff --git a/.github/workflows/cd-stable.yaml b/.github/workflows/cd-stable.yaml
index b4abe2e85f..e3955591a1 100644
--- a/.github/workflows/cd-stable.yaml
+++ b/.github/workflows/cd-stable.yaml
@@ -57,7 +57,7 @@ jobs:
# SLACK_COLOR: "#FF0000"
# SLACK_ICON: https://clipart.world/wp-content/uploads/2021/06/Rocket-Ship-clipart-png.png
# SLACK_MESSAGE: "PR merge by ${{ github.actor }} failed to deploy."
-# SLACK_TITLE: FAILED Stable Deployment for manager-ui
+# SLACK_TITLE: FAILED: Stable Deployment for manager-ui
# SLACK_USERNAME: Deploy Bot
# SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
@@ -73,6 +73,6 @@ jobs:
# SLACK_COLOR: "#FF0000"
# SLACK_ICON: https://clipart.world/wp-content/uploads/2021/06/Rocket-Ship-clipart-png.png
# SLACK_MESSAGE: "PR merge by ${{ github.actor }} has been deployed to stage."
-# SLACK_TITLE: Stable Deployment for manager-ui
+# SLACK_TITLE: STABLE:manager-ui
# SLACK_USERNAME: Deploy Bot
# SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
diff --git a/.github/workflows/cd-stage.yaml b/.github/workflows/cd-stage.yaml
index aad915ff8b..23164ff507 100644
--- a/.github/workflows/cd-stage.yaml
+++ b/.github/workflows/cd-stage.yaml
@@ -72,7 +72,7 @@ jobs:
SLACK_COLOR: "#75BF43"
SLACK_ICON: https://brand.zesty.io/zesty-io-logo.svg
# SLACK_MESSAGE: "PR merge by ${{ github.actor }} has been deployed to stage."
- SLACK_TITLE: "SUCCESS: Stage Deployment of manager-ui"
+ SLACK_TITLE: "STAGE:manager-ui"
SLACK_USERNAME: Deploy Bot
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
@@ -103,39 +103,3 @@ jobs:
run: gh pr create -B beta -H stage --title 'Beta Release' --body 'Created by Github action'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- # unify_dev_history:
- # runs-on: ubuntu-latest
- # # needs: [deploy_stage]
- # steps:
- # - name: Checkout Repo
- # uses: actions/checkout@v4
-
- # - name: Check if PR exists
- # id: check
- # env:
- # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # run: |
- # prs=$(gh pr list \
- # --repo "$GITHUB_REPOSITORY" \
- # --head 'stage' \
- # --base 'dev' \
- # --json title \
- # --jq 'length')
- # if ((prs > 0)); then
- # echo "skip=true" >> "$GITHUB_OUTPUT"
- # fi
-
- # - name: Merge Stage Release to Dev
- # if: "!steps.check.outputs.skip"
- # run: gh pr create -B dev -H stage --title 'Merge Stage:Dev' --body 'Created by Github action'
- # env:
- # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- # unify_dev_history:
- # runs-on: ubuntu-latest
- # steps:
- # - id: create-dev-pr
- # run: gh pr create -B dev -H stage --title 'Merge Stage:Dev' --body 'Update dev history with stage release. PR created and merged by Github action'
- # - id: merge-dev-pr
- # run: gh pr merge --auto --squash ${{steps.create-dev-pr.}}
diff --git a/cypress/e2e/content/content.spec.js b/cypress/e2e/content/content.spec.js
index 6871cd1aa8..9c76b18fa4 100644
--- a/cypress/e2e/content/content.spec.js
+++ b/cypress/e2e/content/content.spec.js
@@ -188,7 +188,7 @@ describe("Content Specs", () => {
it("Number Field", () => {
// NOTE: the timestamp is too large for the 'small int' column in the DB
// limit is 4294967295
- cy.get("#12-9b96ec-tll2gn input[type=number]")
+ cy.get("#12-9b96ec-tll2gn input[type=text]")
.focus()
/*
input type='number 'cannot be empty so rather than whitespace, it'd have a value of 0
diff --git a/package-lock.json b/package-lock.json
index 8f2a2fd6ed..b2cb78b641 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -56,6 +56,7 @@
"react-flatpickr": "^3.10.7",
"react-measure": "^2.5.2",
"react-monaco-editor": "^0.47.0",
+ "react-number-format": "^5.3.1",
"react-redux": "^7.2.4",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
@@ -11843,6 +11844,18 @@
"react": "^17.x"
}
},
+ "node_modules/react-number-format": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.3.1.tgz",
+ "integrity": "sha512-qpYcQLauIeEhCZUZY9jXZnnroOtdy3jYaS1zQ3M1Sr6r/KMOBEIGNIb7eKT19g2N1wbYgFgvDzs19hw5TrB8XQ==",
+ "dependencies": {
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-redux": {
"version": "7.2.8",
"license": "MIT",
@@ -23237,6 +23250,14 @@
"prop-types": "^15.8.1"
}
},
+ "react-number-format": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.3.1.tgz",
+ "integrity": "sha512-qpYcQLauIeEhCZUZY9jXZnnroOtdy3jYaS1zQ3M1Sr6r/KMOBEIGNIb7eKT19g2N1wbYgFgvDzs19hw5TrB8XQ==",
+ "requires": {
+ "prop-types": "^15.7.2"
+ }
+ },
"react-redux": {
"version": "7.2.8",
"requires": {
diff --git a/package.json b/package.json
index a6697fd282..381be98fc2 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
"react-flatpickr": "^3.10.7",
"react-measure": "^2.5.2",
"react-monaco-editor": "^0.47.0",
+ "react-number-format": "^5.3.1",
"react-redux": "^7.2.4",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
diff --git a/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx b/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx
index 033982bfc4..addeee8bdf 100644
--- a/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx
+++ b/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx
@@ -52,6 +52,7 @@ import {
import { FieldTypeDate } from "../../../../../../../shell/components/FieldTypeDate";
import { FieldTypeDateTime } from "../../../../../../../shell/components/FieldTypeDateTime";
import { FieldTypeSort } from "../../../../../../../shell/components/FieldTypeSort";
+import { FieldTypeNumber } from "../../../../../../../shell/components/FieldTypeNumber";
import styles from "./Field.less";
import { MemoryRouter } from "react-router";
@@ -859,16 +860,12 @@ export const Field = ({
case "number":
return (
- onChange(evt.target.value, name)}
- error={errors && Object.values(errors)?.some((error) => !!error)}
+ onChange={onChange}
+ hasError={errors && Object.values(errors)?.some((error) => !!error)}
/>
);
diff --git a/src/apps/media/src/app/components/UploadModal.tsx b/src/apps/media/src/app/components/UploadModal.tsx
index 1d8f8d9382..d7cee6d083 100644
--- a/src/apps/media/src/app/components/UploadModal.tsx
+++ b/src/apps/media/src/app/components/UploadModal.tsx
@@ -27,6 +27,7 @@ import { DnDProvider } from "./DnDProvider";
import { UploadButton } from "./UploadButton";
import CloseIcon from "@mui/icons-material/Close";
import { mediaManagerApi } from "../../../../../shell/services/mediaManager";
+import pluralizeWord from "../../../../../utility/pluralizeWord";
export const UploadModal: FC = () => {
const dispatch = useDispatch();
@@ -227,7 +228,9 @@ const UploadHeaderText = ({ uploads }: UploadHeaderTextProps) => {
{filesUploading?.length > 0
? filesUploading.length
: filesUploaded.length}{" "}
- File{filesUploading.length > 1 && "s"}{" "}
+ {filesUploading?.length > 0
+ ? pluralizeWord("File", filesUploading.length)
+ : pluralizeWord("File", filesUploaded.length)}{" "}
{filesUploading?.length > 0 ? "Uploading" : "Uploaded"}
diff --git a/src/shell/components/FieldTypeNumber.tsx b/src/shell/components/FieldTypeNumber.tsx
new file mode 100644
index 0000000000..5948b28d0e
--- /dev/null
+++ b/src/shell/components/FieldTypeNumber.tsx
@@ -0,0 +1,93 @@
+import { useEffect, useRef } from "react";
+import { IconButton, Stack, TextField } from "@mui/material";
+import RemoveRoundedIcon from "@mui/icons-material/RemoveRounded";
+import AddRoundedIcon from "@mui/icons-material/AddRounded";
+
+import { NumberFormatInput } from "./NumberFormatInput";
+
+type FieldTypeNumberProps = {
+ required: boolean;
+ name: string;
+ value: number;
+ onChange: (value: number, name: string) => void;
+ hasError: boolean;
+};
+export const FieldTypeNumber = ({
+ required,
+ value,
+ onChange,
+ name,
+ hasError,
+}: FieldTypeNumberProps) => {
+ const numberInputRef = useRef(null);
+
+ useEffect(() => {
+ if (value === 0) {
+ numberInputRef.current?.setSelectionRange(1, 1);
+ }
+ }, [value]);
+
+ const modifyNumberValue = (action: "increment" | "decrement") => {
+ if (value.toString().includes("e")) return;
+
+ const integerFractionalSplit = value.toString().split(".");
+
+ switch (action) {
+ case "increment":
+ integerFractionalSplit[0] = (+integerFractionalSplit[0] + 1).toString();
+ break;
+
+ case "decrement":
+ integerFractionalSplit[0] = (+integerFractionalSplit[0] - 1).toString();
+ break;
+
+ default:
+ break;
+ }
+
+ onChange(+integerFractionalSplit.join("."), name);
+ };
+
+ return (
+ {
+ onChange(+evt.target.value?.toString()?.replace(/^0+/, "") ?? 0, name);
+ }}
+ onKeyDown={(evt) => {
+ if ((evt.key === "Backspace" || evt.key === "Delete") && value === 0) {
+ evt.preventDefault();
+ }
+ }}
+ error={hasError}
+ InputProps={{
+ endAdornment: (
+
+ modifyNumberValue("decrement")}
+ >
+
+
+ modifyNumberValue("increment")}
+ >
+
+
+
+ ),
+ inputComponent: NumberFormatInput as any,
+ inputProps: {
+ thousandSeparator: true,
+ valueIsNumericString: true,
+ },
+ }}
+ />
+ );
+};
diff --git a/src/shell/components/NumberFormatInput/index.tsx b/src/shell/components/NumberFormatInput/index.tsx
new file mode 100644
index 0000000000..6efa07b581
--- /dev/null
+++ b/src/shell/components/NumberFormatInput/index.tsx
@@ -0,0 +1,32 @@
+import { forwardRef } from "react";
+import {
+ NumericFormatProps,
+ InputAttributes,
+ NumericFormat,
+} from "react-number-format";
+
+type NumberFormatInputProps = {
+ onChange: (event: { target: { name: string; value: number } }) => void;
+ name: string;
+};
+export const NumberFormatInput = forwardRef<
+ NumericFormatProps,
+ NumberFormatInputProps
+>((props, ref) => {
+ const { onChange, ...other } = props;
+
+ return (
+ {
+ onChange({
+ target: {
+ name: props.name,
+ value: values.floatValue || 0,
+ },
+ });
+ }}
+ />
+ );
+});