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, + }, + }); + }} + /> + ); +});