diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index af93681..5095443 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -82,7 +82,7 @@ jobs: zip -rq ../app.zip * mv ../app.zip . - name: Upload package - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: app-package path: | diff --git a/.github/workflows/subworkflow-deploy.yml b/.github/workflows/subworkflow-deploy.yml deleted file mode 100644 index b0b1b26..0000000 --- a/.github/workflows/subworkflow-deploy.yml +++ /dev/null @@ -1,130 +0,0 @@ -name: "Deploy PR demo" - -on: - workflow_call: - inputs: - ref: - type: string - description: "The commit ref to build" - default: ${{ github.ref }} - required: false - - deskpro-docker-repository: - type: string - description: "Deskpro docker repository" - required: false - default: "registry.deskprodemo.com/deskpro/deskpro-product-dev" - - mysql-docker-repository: - type: string - description: "MySQL docker repository" - required: false - default: "registry.deskprodemo.com/deskpro/deskpro-mysql-dev" - - artifact-name: - type: string - description: "The artifact name to download that contains the package build output" - default: "app-package" - required: false - - secrets: - DESKPRO_SERVICE_TOKEN: { required: true } - DEMO_SERVER_SSH_KEY: { required: true } - DEMO_SERVER_SSH_USER: { required: true } - DEMO_SERVER_SSH_HOST: { required: true } - DOCKER_REPO_USERNAME: { required: true } - DOCKER_REPO_TOKEN: { required: true } - DESKPRO_LICENSE_KEY: { required: true } - DESKPRO_DEMO_FQDN: { required: true } - APP_SETTINGS: { required: true } - -jobs: - deploy: - name: Deploy / Deskpro Demo - timeout-minutes: 10 - runs-on: ubuntu-20.04 - - steps: - - run: | - mkdir -p compose - - curl -fsSL \ - -H "Accept: application/vnd.github.VERSION.raw" \ - -H "Authorization: token ${{ secrets.DESKPRO_SERVICE_TOKEN }}" \ - -o compose/docker-compose.yml \ - https://api.github.com/repos/deskpro/deskpro-product/contents/docker/compose/deskpro-demo/docker-compose.yml - - - name: Create Docker SSH context - uses: deskpro/gh-actions/create-ssh-docker-context@master - with: - context-name: "demo" - private-key: ${{ secrets.DEMO_SERVER_SSH_KEY }} - user: ${{ secrets.DEMO_SERVER_SSH_USER }} - host: ${{ secrets.DEMO_SERVER_SSH_HOST }} - - - name: Switch Docker context - run: | - docker context use demo - - - name: Deploy demo - id: deployment - uses: deskpro/gh-actions/deploy-deskpro-demo@master - with: - compose-project-path: compose - - deskpro-docker-repository: ${{ inputs.deskpro-docker-repository }} - mysql-docker-repository: ${{ inputs.mysql-docker-repository }} - - registry-url: registry.deskprodemo.com - registry-username: ${{ secrets.DOCKER_REPO_USERNAME }} - registry-password: ${{ secrets.DOCKER_REPO_TOKEN }} - - license-key: ${{ secrets.DESKPRO_LICENSE_KEY }} - vhost-domain: ${{ secrets.DESKPRO_DEMO_FQDN }} - - url-resource: "/horizon-ui/app" - - - name: Lookup container ID - id: container - run: | - service_name="${{ steps.deployment.outputs.swarm-stack-name }}_deskpro" - service_id="$(docker service ps -q "${service_name}" | head -1)" - - if [ -z "${service_id}" ]; then - echo "::error title=Deskpro Swarm service ${service_name} not found::Deskpro Swarm service ${service_name} not found" - exit 1 - fi - - container_id="$(docker inspect --format '{{.Status.ContainerStatus.ContainerID}}' "${service_id}")" - echo "id=${container_id}" >> $GITHUB_OUTPUT - - - name: Download package - uses: actions/download-artifact@v3 - with: - name: ${{ inputs.artifact-name }} - path: app-package - - - name: Discover app name - id: app - working-directory: app-package - run: | - app_name=$(jq -r '.name' manifest.json) - echo "name=${app_name}" >> $GITHUB_OUTPUT - - - name: Copy package to container - working-directory: app-package - run: | - docker cp app.zip "${{ steps.container.outputs.id }}:/srv/deskpro/tools/fixtures/resources/custom_app_packages/app.zip" - - - name: Add package - run: | - docker exec "${{ steps.container.outputs.id }}" php /srv/deskpro/tools/fixtures/artisan apps:insert \ - --package=app.zip - - - name: Install package - run: | - docker exec "${{ steps.container.outputs.id }}" php /srv/deskpro/tools/fixtures/artisan apps:install \ - --name="${{ steps.app.outputs.name }}" \ - --settings=${{ toJSON(secrets.APP_SETTINGS) }} \ - --permission-person-ids=1 \ - --uninstall-instances-before-install diff --git a/DESCRIPTION.md b/DESCRIPTION-en-GB.md similarity index 100% rename from DESCRIPTION.md rename to DESCRIPTION-en-GB.md diff --git a/DESCRIPTION-es-ES.md b/DESCRIPTION-es-ES.md new file mode 100644 index 0000000..6d5be46 --- /dev/null +++ b/DESCRIPTION-es-ES.md @@ -0,0 +1,7 @@ +# Scratchpad + +Un bloc de notas que está disponible para ver y editar en todas las partes de Deskpro + +## Descripción + +Toma notas personales rápidamente con la libertad de usar el área de texto para lo que desees, como una lista de tareas, anotar detalles o simplemente notas para ti mismo. diff --git a/LICENSE.md b/LICENSE.md index 04334a7..c275327 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Deskpro +Copyright (c) 2024 Deskpro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/SETUP-en-GB.md b/SETUP-en-GB.md new file mode 100644 index 0000000..ce1ac3e --- /dev/null +++ b/SETUP-en-GB.md @@ -0,0 +1,3 @@ +# Scratch Setup Instructions + +There are no special setup instructions to get this app working. Just install it and all its functionality will be available to you. \ No newline at end of file diff --git a/SETUP-es-ES.md b/SETUP-es-ES.md new file mode 100644 index 0000000..283eb8c --- /dev/null +++ b/SETUP-es-ES.md @@ -0,0 +1,3 @@ +# Instrucciones de configuración de Scratchpad + +No hay instrucciones de configuración especiales para que esta aplicación funcione. Simplemente instálala y toda su funcionalidad estará disponible para ti. diff --git a/SETUP.md b/SETUP.md deleted file mode 100644 index 2fd5c26..0000000 --- a/SETUP.md +++ /dev/null @@ -1,7 +0,0 @@ -# My App Setup Instructions - -1. Build the package with yarn build:package -2. Go on Admin -> Apps & Integration -> Apps -> Available Tab -> Upload App -3. Select your package, which should be inside ./build -4. Click the Scratchpad App, go on Setup and click Install at the bottom -5. For development purposes, you should include the URL where you're running the app. For this, click on Scratchpad App inside the Installed tab, click on developers, toggle on Development mode and Insert the URL, and press Save at the bottom. \ No newline at end of file diff --git a/manifest.json b/manifest.json index c1142dc..b010935 100644 --- a/manifest.json +++ b/manifest.json @@ -8,5 +8,6 @@ "hasDevMode": true, "isSingleInstall": false, "serveUrl": "https://apps-cdn.deskpro-service.com/__name__/__version__", - "targets": [{ "target": "global", "entrypoint": "index.html" }] + "targets": [{ "target": "global", "entrypoint": "index.html" }], + "locales": ["en-GB", "es-ES"] } diff --git a/package.json b/package.json index be94bc8..cd05f06 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "polished": "^4.1.1", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-intl": "^6.8.7", "react-quill": "^2.0.0", "react-resize-observer": "^1.1.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 875d915..1ae2329 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-intl: + specifier: ^6.8.7 + version: 6.8.7(react@18.3.1)(typescript@4.9.5) react-quill: specifier: ^2.0.0 version: 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -383,24 +386,53 @@ packages: '@formatjs/ecma402-abstract@1.11.4': resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==} + '@formatjs/ecma402-abstract@2.2.3': + resolution: {integrity: sha512-aElGmleuReGnk2wtYOzYFmNWYoiWWmf1pPPCYg0oiIQSJj0mjc4eUfzUXaSOJ4S8WzI/cLqnCTWjqz904FT2OQ==} + '@formatjs/fast-memoize@1.2.1': resolution: {integrity: sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==} + '@formatjs/fast-memoize@2.2.3': + resolution: {integrity: sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==} + '@formatjs/icu-messageformat-parser@2.1.0': resolution: {integrity: sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==} + '@formatjs/icu-messageformat-parser@2.9.3': + resolution: {integrity: sha512-9L99QsH14XjOCIp4TmbT8wxuffJxGK8uLNO1zNhLtcZaVXvv626N0s4A2qgRCKG3dfYWx9psvGlFmvyVBa6u/w==} + '@formatjs/icu-skeleton-parser@1.3.6': resolution: {integrity: sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==} + '@formatjs/icu-skeleton-parser@1.8.7': + resolution: {integrity: sha512-fI+6SmS2g7h3srfAKSWa5dwreU5zNEfon2uFo99OToiLF6yxGE+WikvFSbsvMAYkscucvVmTYNlWlaDPp0n5HA==} + '@formatjs/intl-displaynames@5.4.3': resolution: {integrity: sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==} + '@formatjs/intl-displaynames@6.8.4': + resolution: {integrity: sha512-HDVNBspDAOW0yTWluWTPHX2fk/9iBO4oST4R96f/IUaPGsFtjsHrpakwc+XDRPa3U5RniSEU2z34ZY0W78+E6Q==} + '@formatjs/intl-listformat@6.5.3': resolution: {integrity: sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==} + '@formatjs/intl-listformat@7.7.4': + resolution: {integrity: sha512-lipFspH2MZcoeXxR6WSR/Jy9unzJ/iT0w+gbL8vgv25Ap0S9cUtcDVAce4ECEKI1bDtAvEU3b6+9Dha27gAikA==} + '@formatjs/intl-localematcher@0.2.25': resolution: {integrity: sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==} + '@formatjs/intl-localematcher@0.5.7': + resolution: {integrity: sha512-GGFtfHGQVFe/niOZp24Kal5b2i36eE2bNL0xi9Sg/yd0TR8aLjcteApZdHmismP5QQax1cMnZM9yWySUUjJteA==} + + '@formatjs/intl@2.10.14': + resolution: {integrity: sha512-4CA1EO75i/mSMHdjwfpgRj3Rsdsm6WjALeu/nlzYhBmAPxGu/Ha5GIRHAet5SO05TnpmqxmEGOsskWqFm0IeoA==} + peerDependencies: + typescript: ^4.7 || 5 + peerDependenciesMeta: + typescript: + optional: true + '@formatjs/intl@2.2.1': resolution: {integrity: sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==} peerDependencies: @@ -1649,6 +1681,9 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + intl-messageformat@10.7.6: + resolution: {integrity: sha512-IsMU/hqyy3FJwNJ0hxDfY2heJ7MteSuFvcnCebxRp67di4Fhx1gKKE+qS0bBwUF8yXkX9SsPUhLeX/B6h5SKUA==} + intl-messageformat@9.13.0: resolution: {integrity: sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==} @@ -2317,6 +2352,15 @@ packages: typescript: optional: true + react-intl@6.8.7: + resolution: {integrity: sha512-Ocv8Tg6fXqBdVdkkYohQ79T9rJls3G1lmDSjhqHdK9873BdQFLSeITGgwuGWTRBd6Mg5FL33TBen4FtujCTP0g==} + peerDependencies: + react: ^16.6.0 || 17 || 18 + typescript: ^4.7 || 5 + peerDependenciesMeta: + typescript: + optional: true + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -3209,38 +3253,87 @@ snapshots: '@formatjs/ecma402-abstract@1.11.4': dependencies: '@formatjs/intl-localematcher': 0.2.25 - tslib: 2.3.1 + tslib: 2.4.1 + + '@formatjs/ecma402-abstract@2.2.3': + dependencies: + '@formatjs/fast-memoize': 2.2.3 + '@formatjs/intl-localematcher': 0.5.7 + tslib: 2.4.1 '@formatjs/fast-memoize@1.2.1': dependencies: - tslib: 2.3.1 + tslib: 2.4.1 + + '@formatjs/fast-memoize@2.2.3': + dependencies: + tslib: 2.4.1 '@formatjs/icu-messageformat-parser@2.1.0': dependencies: '@formatjs/ecma402-abstract': 1.11.4 '@formatjs/icu-skeleton-parser': 1.3.6 - tslib: 2.3.1 + tslib: 2.4.1 + + '@formatjs/icu-messageformat-parser@2.9.3': + dependencies: + '@formatjs/ecma402-abstract': 2.2.3 + '@formatjs/icu-skeleton-parser': 1.8.7 + tslib: 2.4.1 '@formatjs/icu-skeleton-parser@1.3.6': dependencies: '@formatjs/ecma402-abstract': 1.11.4 - tslib: 2.3.1 + tslib: 2.4.1 + + '@formatjs/icu-skeleton-parser@1.8.7': + dependencies: + '@formatjs/ecma402-abstract': 2.2.3 + tslib: 2.4.1 '@formatjs/intl-displaynames@5.4.3': dependencies: '@formatjs/ecma402-abstract': 1.11.4 '@formatjs/intl-localematcher': 0.2.25 - tslib: 2.3.1 + tslib: 2.4.1 + + '@formatjs/intl-displaynames@6.8.4': + dependencies: + '@formatjs/ecma402-abstract': 2.2.3 + '@formatjs/intl-localematcher': 0.5.7 + tslib: 2.4.1 '@formatjs/intl-listformat@6.5.3': dependencies: '@formatjs/ecma402-abstract': 1.11.4 '@formatjs/intl-localematcher': 0.2.25 - tslib: 2.3.1 + tslib: 2.4.1 + + '@formatjs/intl-listformat@7.7.4': + dependencies: + '@formatjs/ecma402-abstract': 2.2.3 + '@formatjs/intl-localematcher': 0.5.7 + tslib: 2.4.1 '@formatjs/intl-localematcher@0.2.25': dependencies: - tslib: 2.3.1 + tslib: 2.4.1 + + '@formatjs/intl-localematcher@0.5.7': + dependencies: + tslib: 2.4.1 + + '@formatjs/intl@2.10.14(typescript@4.9.5)': + dependencies: + '@formatjs/ecma402-abstract': 2.2.3 + '@formatjs/fast-memoize': 2.2.3 + '@formatjs/icu-messageformat-parser': 2.9.3 + '@formatjs/intl-displaynames': 6.8.4 + '@formatjs/intl-listformat': 7.7.4 + intl-messageformat: 10.7.6 + tslib: 2.4.1 + optionalDependencies: + typescript: 4.9.5 '@formatjs/intl@2.2.1(typescript@4.9.5)': dependencies: @@ -3250,7 +3343,7 @@ snapshots: '@formatjs/intl-displaynames': 5.4.3 '@formatjs/intl-listformat': 6.5.3 intl-messageformat: 9.13.0 - tslib: 2.3.1 + tslib: 2.4.1 optionalDependencies: typescript: 4.9.5 @@ -4702,12 +4795,19 @@ snapshots: hasown: 2.0.2 side-channel: 1.0.6 + intl-messageformat@10.7.6: + dependencies: + '@formatjs/ecma402-abstract': 2.2.3 + '@formatjs/fast-memoize': 2.2.3 + '@formatjs/icu-messageformat-parser': 2.9.3 + tslib: 2.4.1 + intl-messageformat@9.13.0: dependencies: '@formatjs/ecma402-abstract': 1.11.4 '@formatjs/fast-memoize': 1.2.1 '@formatjs/icu-messageformat-parser': 2.1.0 - tslib: 2.3.1 + tslib: 2.4.1 is-arguments@1.1.1: dependencies: @@ -5586,7 +5686,23 @@ snapshots: hoist-non-react-statics: 3.3.2 intl-messageformat: 9.13.0 react: 18.3.1 - tslib: 2.3.1 + tslib: 2.4.1 + optionalDependencies: + typescript: 4.9.5 + + react-intl@6.8.7(react@18.3.1)(typescript@4.9.5): + dependencies: + '@formatjs/ecma402-abstract': 2.2.3 + '@formatjs/icu-messageformat-parser': 2.9.3 + '@formatjs/intl': 2.10.14(typescript@4.9.5) + '@formatjs/intl-displaynames': 6.8.4 + '@formatjs/intl-listformat': 7.7.4 + '@types/hoist-non-react-statics': 3.3.5 + '@types/react': 17.0.80 + hoist-non-react-statics: 3.3.2 + intl-messageformat: 10.7.6 + react: 18.3.1 + tslib: 2.4.1 optionalDependencies: typescript: 4.9.5 diff --git a/src/App.tsx b/src/App.tsx index a595fdf..c2a80c7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,11 +4,14 @@ import { Main } from "./pages/Main"; import "@deskpro/deskpro-ui/dist/deskpro-ui.css"; import "@deskpro/deskpro-ui/dist/deskpro-custom-icons.css"; import "simplebar/dist/simplebar.min.css"; +import TranslatedApp from "./TranslatedApp"; function App() { return ( -
+ +
+ ); } diff --git a/src/TranslatedApp.tsx b/src/TranslatedApp.tsx new file mode 100644 index 0000000..85d1c73 --- /dev/null +++ b/src/TranslatedApp.tsx @@ -0,0 +1,20 @@ +import { useDeskproLatestAppContext } from "@deskpro/app-sdk"; +import type { ReactNode } from "react"; +import { IntlProvider } from "react-intl"; +import locale_en_GB from "../translations/en-GB.json"; +import locale_es_ES from "../translations/es-ES.json"; + +const locales = { + 'en-GB': locale_en_GB, + 'es-ES': locale_es_ES, +} as const; + +export default function TranslatedApp(props: { children: ReactNode }) { + const { context } = useDeskproLatestAppContext(); + const locale = context?.data?.app?.locale ?? 'en-GB'; + const messages = locales[locale as keyof typeof locales]; + + return + <>{props.children} + +} \ No newline at end of file diff --git a/src/pages/Main.tsx b/src/pages/Main.tsx index 5173de7..d50d7d0 100644 --- a/src/pages/Main.tsx +++ b/src/pages/Main.tsx @@ -9,13 +9,13 @@ import ReactQuill from "react-quill"; import "react-quill/dist/quill.snow.css"; import useDebounce from "../utils/debounce"; import { Button, H1, Scrollbar, Stack } from "@deskpro/deskpro-ui"; +import { FormattedMessage } from 'react-intl'; export const Main = () => { const { client } = useDeskproAppClient(); - const [text, setText] = useState(""); const [ranFirstTime, setRanFirstTime] = useState(false); - const [saveStatus, setSaveStatus] = useState("Saved"); + const [saveStatus, setSaveStatus] = useState<"saved" | "saving">("saved"); const { debouncedValue, setDebouncedValue } = useDebounce(text, 500); useInitialisedDeskproAppClient((client) => { @@ -31,9 +31,9 @@ export const Main = () => { useInitialisedDeskproAppClient( (client) => { if (ranFirstTime) { - setSaveStatus("Saving"); + setSaveStatus("saving"); client.setUserState("scratchpad", debouncedValue); - setSaveStatus("Saved"); + setSaveStatus("saved"); } }, [debouncedValue] @@ -48,7 +48,7 @@ export const Main = () => { return ( {ranFirstTime && ( - + { marginLeft: "3px", }} size="large" - text="Clear" - > - {debouncedValue === text ? ( -

{saveStatus}

- ) : ( -

Typing...

- )} + text={} + /> +

+ +

diff --git a/translations/en-GB.json b/translations/en-GB.json new file mode 100644 index 0000000..239d2f7 --- /dev/null +++ b/translations/en-GB.json @@ -0,0 +1,6 @@ +{ + "state.saved": "Saved", + "state.saving": "Saving", + "state.typing": "Typing...", + "button.clear": "Clear" +} \ No newline at end of file diff --git a/translations/es-ES.json b/translations/es-ES.json new file mode 100644 index 0000000..bbfeb33 --- /dev/null +++ b/translations/es-ES.json @@ -0,0 +1,6 @@ +{ + "state.saved": "Guardado", + "state.saving": "Guardando", + "state.typing": "Escribiendo...", + "button.clear": "Borrar" +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index ceb7288..a7476af 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -19,8 +19,8 @@ export default defineConfig({ hook: "writeBundle", targets: [ { src: "./manifest.json", dest: "./dist/" }, - { src: "./DESCRIPTION.md", dest: "./dist/" }, - { src: "./SETUP.md", dest: "./dist/" }, + { src: "./DESCRIPTION*.md", dest: "./dist/" }, + { src: "./SETUP*.md", dest: "./dist/" }, { src: "./icon.svg", dest: "./dist/" }, { src: "./docs", dest: "./dist/" }, ],