From 3b92950829db34890a86fa14739f2b02105653c2 Mon Sep 17 00:00:00 2001 From: Kobina Koomson Date: Wed, 4 Sep 2024 21:11:59 +0000 Subject: [PATCH] Canary -> main (#389) * fix: Will now require community selection before subscription * feat: Add time to one event page & change image on event card to contain * fix: fallback for formatting * Fix: Add Notification for Failed Campaign Subscription API Call * Bump BUILD_VERSION to 1.0.4 * fixed: Fixed date field changine to text * Bump BUILD_VERSION to 1.0.5 * Bump BUILD_VERSION to 1.0.6 * Bump BUILD_VERSION to 1.0.7 * Fix: address formatting in AddToGoogleCalendar component The AddToGoogleCalendar component has been updated to correctly format addresses with state information. The state value is now correctly retrieved and appended to the address string, fixing previous issues where the state data was not properly included. * Fix: address formatting in AddToGoogleCalendar component The AddToGoogleCalendar component has been updated to correctly format addresses with state information. The state value is now correctly retrieved and appended to the address string, fixing previous issues where the state data was not properly included. * Bump BUILD_VERSION to 1.0.8 * scraped static texts on all pages * got text from modals as well * added common languages offered * setting up redux for manual language selection * hooked dropdown to redux data * feat: lang selection working with dummy data set. Selection persists too * whole of homepage now depends on static text object * interactions, comment box, events and testimonial section all looking good * one testimonial, one events page :fire: * several changes json object for static text and testing with dummy translations * language is now automatically added to apiCall * fixed bug with comment component translation * more translations, and tweaks to page json structure * testimonial form now translated too * getting started with languages offered page * still setting up multi select on dropdown. checkbox in dropdown is not checking yet * removing language works locally * setup admin lang selector and docked it at the top of campaign edit page * dropdown for language context on admin side now works with admin selected languages * remove log * Now changed to toggles * removed comments and added translation for fallback text * removed logs * edited toggles * add language back in there * sort languages * translated more fallback texts * translated preview mode & events too * paused on admin tans for now * removed comments and limited languages * now loads language list from api * better config log * version number bump * point to dev * Bump BUILD_VERSION to 1.1.1 * user language * remove about * version number resolution * upgrade * Bump BUILD_VERSION to 1.1.4 * en-us * Bump BUILD_VERSION to 1.1.5 * toggling languages works for admins * client side loads from toggle list * dont show ribbon when no list is provided * dash will be provided if only one enabled language * fix nav issue * english cant be toggled, several tweaks to frontend logic * pre testimonial translations setup for homepage area * translated more dialogs * resolved some circular imports * cp for language selectino modal * language modal setup with several bug fixes also done * fixed testimonial form translation & height issues * now added flags * Bump BUILD_VERSION to 1.1.6 * squashed conflicts * Bump BUILD_VERSION to 1.1.7 * added caret * fixed single and no language logic * bump * Bump BUILD_VERSION to 1.1.9 * now looks for language only when data is loaded * removed logs * optional lang * Bump BUILD_VERSION to 1.1.10 * made messenger pull from local storage (#347) * Bump BUILD_VERSION to 1.1.11 * Prevent language override for admin users This change ensures that the user language parameter is not appended to the API call when the user is an admin. This avoids any conflict or unintended behavior for admin-level operations, enhancing the robustness of language handling in the messaging API. * Add optional chaining in various string splitting operations Refactor the code to use optional chaining for string split operations to ensure error prevention on null or undefined values. This change improves the robustness of the code, particularly for scenarios where split might be called on potentially nullable strings. * Bump BUILD_VERSION to 1.1.12 * Dev -> Canary (#349) * scraped static texts on all pages * got text from modals as well * added common languages offered * setting up redux for manual language selection * hooked dropdown to redux data * feat: lang selection working with dummy data set. Selection persists too * whole of homepage now depends on static text object * interactions, comment box, events and testimonial section all looking good * one testimonial, one events page :fire: * several changes json object for static text and testing with dummy translations * language is now automatically added to apiCall * fixed bug with comment component translation * more translations, and tweaks to page json structure * testimonial form now translated too * getting started with languages offered page * still setting up multi select on dropdown. checkbox in dropdown is not checking yet * removing language works locally * setup admin lang selector and docked it at the top of campaign edit page * dropdown for language context on admin side now works with admin selected languages * remove log * Now changed to toggles * removed comments and added translation for fallback text * removed logs * edited toggles * add language back in there * sort languages * translated more fallback texts * translated preview mode & events too * paused on admin tans for now * removed comments and limited languages * now loads language list from api * better config log * version number bump * point to dev * Bump BUILD_VERSION to 1.1.1 * user language * remove about * version number resolution * upgrade * Bump BUILD_VERSION to 1.1.4 * en-us * Bump BUILD_VERSION to 1.1.5 * toggling languages works for admins * client side loads from toggle list * dont show ribbon when no list is provided * dash will be provided if only one enabled language * fix nav issue * english cant be toggled, several tweaks to frontend logic * pre testimonial translations setup for homepage area * translated more dialogs * resolved some circular imports * cp for language selectino modal * language modal setup with several bug fixes also done * fixed testimonial form translation & height issues * now added flags * Bump BUILD_VERSION to 1.1.6 * squashed conflicts * Bump BUILD_VERSION to 1.1.7 * added caret * fixed single and no language logic * bump * Bump BUILD_VERSION to 1.1.9 * now looks for language only when data is loaded * removed logs * optional lang * Bump BUILD_VERSION to 1.1.10 * made messenger pull from local storage (#347) * Bump BUILD_VERSION to 1.1.11 * Prevent language override for admin users This change ensures that the user language parameter is not appended to the API call when the user is an admin. This avoids any conflict or unintended behavior for admin-level operations, enhancing the robustness of language handling in the messaging API. * Add optional chaining in various string splitting operations Refactor the code to use optional chaining for string split operations to ensure error prevention on null or undefined values. This change improves the robustness of the code, particularly for scenarios where split might be called on potentially nullable strings. * Bump BUILD_VERSION to 1.1.12 --------- Co-authored-by: Version Update Bot Co-authored-by: Tahiru Abdullai * in the process of setting up custom loading messages to inform user of unsupported languages * resolved conflict with chosen language that is not supported by campaign * separating languages from language obj * initial box now translates * about only available from english * capture weird screen dimensions with media queries * blanket notification now in approuter * tighter media query * eventos * more date objs translated too * No preferred language should fallback to english * Bump BUILD_VERSION to 1.1.13 * Fix: Broken community portal link (#379) * Add dynamic URL support for community portal Updated the COMMUNITY_PORTAL_URL to dynamically generate the base URL depending on the environment. Added a utility function `getCommunityPortalBaseURL` to handle this logic. * Refactor community portal URL logic Consolidate community portal URLs into a single `BASE_URL` constant. This change simplifies URL management and reduces redundancy in the codebase. The `getCommunityPortalBaseURL` function has been removed and replaced with a cleaner variable definition. * Fix incorrect branching condition for production environment The `if (IS_PROD)` condition has been changed to `else if (IS_PROD)` to ensure it is mutually exclusive with previous conditions. This prevents the production URL assignment from being skipped if a prior condition is satisfied. * Bump BUILD_VERSION to 1.1.14 * fixed other, and doubly sending user language * filter instead of map * fixed "other" on sharing modal * Bump BUILD_VERSION to 1.1.15 * translated comment section * news letter trans * translated a few toasts * remove logs * Bump BUILD_VERSION to 1.1.16 * Bump BUILD_VERSION to 1.1.17 * spanish continue * added the link for volunteering * manual bump * Bump BUILD_VERSION to 1.1.19 * Dev -> Canary (#384) * scraped static texts on all pages * got text from modals as well * added common languages offered * setting up redux for manual language selection * hooked dropdown to redux data * feat: lang selection working with dummy data set. Selection persists too * whole of homepage now depends on static text object * interactions, comment box, events and testimonial section all looking good * one testimonial, one events page :fire: * several changes json object for static text and testing with dummy translations * language is now automatically added to apiCall * fixed bug with comment component translation * more translations, and tweaks to page json structure * testimonial form now translated too * getting started with languages offered page * still setting up multi select on dropdown. checkbox in dropdown is not checking yet * removing language works locally * setup admin lang selector and docked it at the top of campaign edit page * dropdown for language context on admin side now works with admin selected languages * remove log * Now changed to toggles * removed comments and added translation for fallback text * removed logs * edited toggles * add language back in there * sort languages * translated more fallback texts * translated preview mode & events too * paused on admin tans for now * removed comments and limited languages * now loads language list from api * better config log * version number bump * point to dev * Bump BUILD_VERSION to 1.1.1 * user language * remove about * version number resolution * upgrade * Bump BUILD_VERSION to 1.1.4 * en-us * Bump BUILD_VERSION to 1.1.5 * toggling languages works for admins * client side loads from toggle list * dont show ribbon when no list is provided * dash will be provided if only one enabled language * fix nav issue * english cant be toggled, several tweaks to frontend logic * pre testimonial translations setup for homepage area * translated more dialogs * resolved some circular imports * cp for language selectino modal * language modal setup with several bug fixes also done * fixed testimonial form translation & height issues * now added flags * Bump BUILD_VERSION to 1.1.6 * squashed conflicts * Bump BUILD_VERSION to 1.1.7 * added caret * fixed single and no language logic * bump * Bump BUILD_VERSION to 1.1.9 * now looks for language only when data is loaded * removed logs * optional lang * Bump BUILD_VERSION to 1.1.10 * made messenger pull from local storage (#347) * Bump BUILD_VERSION to 1.1.11 * Prevent language override for admin users This change ensures that the user language parameter is not appended to the API call when the user is an admin. This avoids any conflict or unintended behavior for admin-level operations, enhancing the robustness of language handling in the messaging API. * Add optional chaining in various string splitting operations Refactor the code to use optional chaining for string split operations to ensure error prevention on null or undefined values. This change improves the robustness of the code, particularly for scenarios where split might be called on potentially nullable strings. * Bump BUILD_VERSION to 1.1.12 * in the process of setting up custom loading messages to inform user of unsupported languages * resolved conflict with chosen language that is not supported by campaign * separating languages from language obj * initial box now translates * about only available from english * capture weird screen dimensions with media queries * blanket notification now in approuter * tighter media query * eventos * more date objs translated too * No preferred language should fallback to english * Bump BUILD_VERSION to 1.1.13 * Fix: Broken community portal link (#379) * Add dynamic URL support for community portal Updated the COMMUNITY_PORTAL_URL to dynamically generate the base URL depending on the environment. Added a utility function `getCommunityPortalBaseURL` to handle this logic. * Refactor community portal URL logic Consolidate community portal URLs into a single `BASE_URL` constant. This change simplifies URL management and reduces redundancy in the codebase. The `getCommunityPortalBaseURL` function has been removed and replaced with a cleaner variable definition. * Fix incorrect branching condition for production environment The `if (IS_PROD)` condition has been changed to `else if (IS_PROD)` to ensure it is mutually exclusive with previous conditions. This prevents the production URL assignment from being skipped if a prior condition is satisfied. * Bump BUILD_VERSION to 1.1.14 * fixed other, and doubly sending user language * filter instead of map * fixed "other" on sharing modal * Bump BUILD_VERSION to 1.1.15 * translated comment section * news letter trans * translated a few toasts * remove logs * Bump BUILD_VERSION to 1.1.16 * Bump BUILD_VERSION to 1.1.17 * spanish continue * added the link for volunteering * manual bump * Bump BUILD_VERSION to 1.1.19 --------- Co-authored-by: frimpongopoku Co-authored-by: Version Update Bot Co-authored-by: Tahiru Abdullai Co-authored-by: Abdullai Tahiru <42780120+abdullai-t@users.noreply.github.com> * fix: Static texts translation (#385) * Add notification messages and headers to multiple languages Included success and error notifications for testimonials and comments in English, Spanish, Portuguese, and French. Added new headers for sections such as "Newsletter," "Getting Started," and "Meet the Coaches" across all supported languages. * Update coach section title to include static text Modified the `CoachesSectionWithFilters` component to display the `staticT?.title` in the section title if customization is not available. This ensures a default title is shown even if customization data is missing. * Use static text variables for user notifications Replaced hardcoded text strings with static text variables in both CommentComponentForModal.js and Footer.js to improve maintainability and support for internationalization. This ensures that all user-facing text can be easily managed and translated as needed. * Enhance localization support with static text integration Improved localization by integrating static text placeholders across multiple pages. This includes adjustments to trigger functions, form notifications, and section titles to utilize static text sources when available. * Update user testimonial formatting Modified the format for displaying the user's company name in testimonials to include a static translation key. This ensures the word 'from' can be localized correctly based on user settings. * Remove TODO comment in Footer.js Eliminated an outdated TODO comment related to translation help text in the footer component. This will improve code readability and reduce unnecessary clutter. * Update text property access in components Modified `CoachesSectionWithFilters.js` and `Footer.js` to access the `text` property of nested objects in the `title` and `description` fields. This ensures the correct text is displayed based on the provided customization or static data. * Refactor JSON statics structure to improve consistency Updated the internationalization JSON structure to nest titles and descriptions under 'text' keys. This change ensures a consistent format across multiple sections and languages, enhancing maintainability and readability of the code. Adjusted relevant component render logic to accommodate this new structure. * Bump BUILD_VERSION to 1.1.20 * Fix static text translation (#386) * Add notification messages and headers to multiple languages Included success and error notifications for testimonials and comments in English, Spanish, Portuguese, and French. Added new headers for sections such as "Newsletter," "Getting Started," and "Meet the Coaches" across all supported languages. * Update coach section title to include static text Modified the `CoachesSectionWithFilters` component to display the `staticT?.title` in the section title if customization is not available. This ensures a default title is shown even if customization data is missing. * Use static text variables for user notifications Replaced hardcoded text strings with static text variables in both CommentComponentForModal.js and Footer.js to improve maintainability and support for internationalization. This ensures that all user-facing text can be easily managed and translated as needed. * Enhance localization support with static text integration Improved localization by integrating static text placeholders across multiple pages. This includes adjustments to trigger functions, form notifications, and section titles to utilize static text sources when available. * Update user testimonial formatting Modified the format for displaying the user's company name in testimonials to include a static translation key. This ensures the word 'from' can be localized correctly based on user settings. * Remove TODO comment in Footer.js Eliminated an outdated TODO comment related to translation help text in the footer component. This will improve code readability and reduce unnecessary clutter. * Update text property access in components Modified `CoachesSectionWithFilters.js` and `Footer.js` to access the `text` property of nested objects in the `title` and `description` fields. This ensures the correct text is displayed based on the provided customization or static data. * Refactor JSON statics structure to improve consistency Updated the internationalization JSON structure to nest titles and descriptions under 'text' keys. This change ensures a consistent format across multiple sections and languages, enhancing maintainability and readability of the code. Adjusted relevant component render logic to accommodate this new structure. * Fix comment box trigger in TechnologyFullViewPage Added a second parameter to the triggerCommentBox function to ensure the modal's static texts are passed correctly. This resolves an issue where the comment box was not displaying all necessary information. * Bump BUILD_VERSION to 1.1.21 * Add Spanish and Portuguese translations to internationalization This commit enhances the internationalization feature by adding Spanish and Portuguese translations to various UI elements. These translations cover toasts, forms, loaders, footers, modals, pages, and interactions, enabling a wider audience reach and improving user experience. * language selector will not show in PROD (#388) --------- Co-authored-by: frimpongopoku Co-authored-by: Tahiru Abdullai Co-authored-by: Abdullai Tahiru <42780120+abdullai-t@users.noreply.github.com> Co-authored-by: Version Update Bot --- .gitignore | 2 + package.json | 2 +- .../campaign-details-and-preview.js | 87 +- .../AddOfferedLanguages.js | 156 ++ .../internationalization/ToggleLanguage.js | 40 + src/api/messenger.js | 40 +- src/assets/styles/tablet-ish.css | 14 + src/assets/styles/universal.css | 1 + src/components/SmartRichText.js | 14 +- src/components/data-table/TableFilters.js | 4 +- .../language/LanguageSelectionModal.js | 43 + src/components/language/LanguageSelector.js | 106 + src/components/modal/CustomModal.js | 3 +- src/components/navbar/AppNavigationBar.js | 41 +- src/components/pieces/BlanketNotification.js | 58 + src/components/toggle-switch/ToggleSwitch.js | 24 + .../toggle-switch/toggle-switch.css | 35 + src/config/config.json | 2 +- src/config/firebase/admin/fire-config.js | 2 +- src/helpers/utils/index.js | 18 +- src/helpers/utils/string.js | 16 +- src/index.css | 55 +- src/layout-components/sidebarMenu.js | 21 +- src/redux/actions/actions.js | 140 +- src/redux/reducers/reducers.js | 98 +- src/redux/reducers/state.js | 29 +- src/redux/redux-action-types.js | 9 + src/routes/AppRouter.js | 34 +- src/user-portal/pages/banner/Banner.js | 3 +- src/user-portal/pages/banner/Hero.js | 4 +- .../coaches/CoachesSectionWithFilters.js | 10 +- .../commenting/CommentComponentForModal.js | 25 +- .../pages/events/AddToGoogleCalendar.js | 5 +- src/user-portal/pages/events/EventBox.js | 27 +- .../pages/events/EventsSectionWithFilters.js | 13 +- src/user-portal/pages/events/OneEvent.js | 20 +- src/user-portal/pages/footer/Footer.js | 63 +- .../pages/forms/CommunitySelector.js | 20 +- src/user-portal/pages/forms/GetHelpForm.js | 9 +- src/user-portal/pages/forms/JoinUsForm.js | 28 +- .../getting-started/GettingStartedSection.js | 25 +- .../pages/getting-started/NewOneBox.js | 18 +- .../pages/getting-started/OneBox.js | 17 +- .../pages/landing-page/CampaignNotLive.js | 24 +- src/user-portal/pages/landing-page/DoMore.js | 10 +- .../pages/landing-page/LandingPage.js | 55 +- .../pages/landing-page/RoamingBox.js | 200 +- src/user-portal/pages/sharing/ShareBox.js | 14 +- .../technology/CommentDeleteConfirmation.js | 16 +- .../pages/technology/InteractionsPanel.js | 19 +- .../pages/technology/OneTechEventSection.js | 6 +- .../technology/OneTechMeetTheCoachesSetion.js | 34 +- .../technology/OneTechTestimonialsSection.js | 38 +- .../pages/technology/TakeActionSection.js | 19 +- .../technology/TechnologyFullViewPage.js | 102 +- src/user-portal/pages/technology/Vendors.js | 50 +- .../pages/technology/WhySection.js | 4 +- .../pages/testimonials/NewTestimonialForm.js | 32 +- .../pages/testimonials/OneTestimonial.js | 31 +- .../pages/testimonials/TestimonialBox.js | 9 +- .../TestimonialSectionWithFilters.js | 17 +- src/utils/Constants.js | 5 + src/utils/api_call.js | 25 +- .../internationalization/json-statics.js | 1968 +++++++++++++++++ src/utils/internationalization/languages.js | 11 + src/utils/utils.js | 65 +- src/views/technology-edit-view.js | 47 +- 67 files changed, 3560 insertions(+), 622 deletions(-) create mode 100644 src/admin-portal/internationalization/AddOfferedLanguages.js create mode 100644 src/admin-portal/internationalization/ToggleLanguage.js create mode 100644 src/assets/styles/tablet-ish.css create mode 100644 src/components/language/LanguageSelectionModal.js create mode 100644 src/components/language/LanguageSelector.js create mode 100644 src/components/pieces/BlanketNotification.js create mode 100644 src/components/toggle-switch/ToggleSwitch.js create mode 100644 src/components/toggle-switch/toggle-switch.css create mode 100644 src/utils/internationalization/json-statics.js create mode 100644 src/utils/internationalization/languages.js diff --git a/.gitignore b/.gitignore index 172a440a..1f27eac5 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* .idea/ +.env.development +.env.development diff --git a/package.json b/package.json index 236a1bd8..7efaa9fa 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "start": "react-scripts start", + "start": "PORT=3002 react-scripts start", "build": "CI=false react-scripts build", "build:dev": "NODE_ENV=development REACT_APP_TARGET_ENV=development CI=false react-scripts build", "build:canary": "NODE_ENV=canary REACT_APP_TARGET_ENV=canary CI=false react-scripts build", diff --git a/src/admin-portal/create-campaign/campaign-details-and-preview.js b/src/admin-portal/create-campaign/campaign-details-and-preview.js index 35597e51..4da749e1 100644 --- a/src/admin-portal/create-campaign/campaign-details-and-preview.js +++ b/src/admin-portal/create-campaign/campaign-details-and-preview.js @@ -6,12 +6,18 @@ import LandingPage from "../../user-portal/pages/landing-page/LandingPage"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faExternalLink } from "@fortawesome/free-solid-svg-icons"; import { useCampaignContext } from "../../hooks/use-campaign-context"; +import ToggleLanguage from "../internationalization/ToggleLanguage"; -export function CampaignDetailsAndPreview ({setStep,}) { +export function CampaignDetailsAndPreview({ setStep }) { const [activeTab, setActiveTab] = useState(campaignPages[0].name); const [preview, setPreview] = useState(false); - const {campaignDetails,originalCampaignDetails,lists,handleCampaignDetailsChange : setCampaignDetails} = useCampaignContext(); + const { + campaignDetails, + originalCampaignDetails, + lists, + handleCampaignDetailsChange: setCampaignDetails, + } = useCampaignContext(); return ( <> @@ -20,11 +26,15 @@ export function CampaignDetailsAndPreview ({setStep,}) {

{campaignDetails.title}

+ {/* */} -
@@ -36,18 +46,15 @@ export function CampaignDetailsAndPreview ({setStep,}) {
- { - campaignPages?.map((page) => ( -
setActiveTab(page?.name)} - > -
- {page?.name} -
-
- ))} + {campaignPages?.map((page) => ( +
setActiveTab(page?.name)} + > +
{page?.name}
+
+ ))}
@@ -56,33 +63,29 @@ export function CampaignDetailsAndPreview ({setStep,}) { {/*region Body: Content goes here*/} - { - campaignPages?.map((tab) => { - return ( - activeTab === tab?.name && ( - - ) - ); - })} + {campaignPages?.map((tab) => { + return ( + activeTab === tab?.name && ( + + ) + ); + })} - - { - preview && ( - - - - ) - } + {preview && ( + + + + )} - ) + ); } diff --git a/src/admin-portal/internationalization/AddOfferedLanguages.js b/src/admin-portal/internationalization/AddOfferedLanguages.js new file mode 100644 index 00000000..f0a0ca65 --- /dev/null +++ b/src/admin-portal/internationalization/AddOfferedLanguages.js @@ -0,0 +1,156 @@ +import React, { useEffect, useState } from "react"; +import { AdminLayout } from "../../layouts/admin-layout"; +import { Container, Row, ToggleButton } from "react-bootstrap"; +import { MultiSelect } from "react-multi-select-component"; +import { LANGUAGES } from "../../utils/internationalization/languages"; +import { useDispatch, useSelector } from "react-redux"; +import { loadLanguagesAction, updateOfferedLanguageAction } from "src/redux/actions/actions"; +import Button from "src/components/admin-components/Button"; +import ToggleSwitch from "src/components/toggle-switch/ToggleSwitch"; +import Loading from "src/components/pieces/Loading"; +import { apiCall } from "src/api/messenger"; +export const getOfferedForThisCampaign = (objFromRedux, id) => { + if (!objFromRedux) return DEFAULT_OFFERED_PER_CAMPAIGN; + return objFromRedux[id]; +}; + +const DEFAULT_OFFERED_PER_CAMPAIGN = [{ label: "English (US)", value: "en-US" }]; +function AddOfferedLanguages({ campaignDetails: campaign }) { + const [loading, setLoading] = useState(false); + const [updateLoading, setUpdateLoading] = useState(false); + const [error, setError] = useState(""); + const [trackChanges, setChanges] = useState({}); + const languages = useSelector((state) => state?.languageList); + languages?.sort((a, b) => a?.name?.localeCompare(b?.name)); + + // const cOffered = useSelector((state) => state?.campaignOfferedLanguages); + const dispatch = useDispatch(); + const campaignId = campaign?.id; + + const putLanguageListInRedux = (data) => { + return dispatch(loadLanguagesAction(data)); + }; + + const includeNewLanguage = (key, status) => { + setChanges({ ...trackChanges, [key]: status }); + }; + + const pushChanges = () => { + setUpdateLoading(true); + const body = { campaign_id: campaignId, supported_languages: JSON.stringify(trackChanges) }; + apiCall("campaigns.supported_languages.update", body).then((res) => { + setUpdateLoading(false); + if (!res?.success) return console.log("Error Saving Changes: ", res?.error); + putLanguageListInRedux(res?.data); + }); + }; + + const fetchEssentials = () => { + Promise.all([apiCall("campaigns.supported_languages.list", { campaign_id: campaignId })]).then(([langList]) => { + setLoading(false); + if (!langList?.success) { + return setError(langList?.error || "Sorry, could not load list of languages"); + } + putLanguageListInRedux(langList?.data); + // updateInRedux(offeredList?.data); + }); + }; + + useEffect(() => { + if (!languages?.length) fetchEssentials(); + else setLoading(false); + }, []); + + if (error) return

{error}

; + if (loading) return ; + + return ( + // +
+ + {/* */} + {/*

Add Offered Languages

*/} +

Add all the translation languages that you want to offer on your campaign site here

+
+ {/*
*/} + {/* { + return { + value, + label, + }; + })} + onChange={(vals) => updateInRedux(vals)} + valueRenderer={(selected, _options) => { + if (selected.length === 0) return "Select languages"; + if (selected.length === _options.length) return "All languages selected"; + if (selected.length > 5) return `${selected.length} languages Selected`; + return selected + ?.map(({ label }) => label) + .join(", ") + .concat(" Selected"); + }} + labelledBy="Select" + /> */} + +
+
+
LANGUAGES
+
Toggle ON/OFF
+
+ {languages?.map(({ name: label, code: k, is_active }) => { + const isEnglish = k === "en-US"; + return ( +
+ {label} + +
+ {!isEnglish && includeNewLanguage(k, state)} ON={is_active} />} +
+ {/* removeLang(lang?.value)} + style={{ marginLeft: "auto", color: "#c83131", fontSize: 16 }} + > + Remove + */} +
+ ); + })} +
+ +
+ +
+
+ +
+ // + ); +} + +export default AddOfferedLanguages; diff --git a/src/admin-portal/internationalization/ToggleLanguage.js b/src/admin-portal/internationalization/ToggleLanguage.js new file mode 100644 index 00000000..e17e59d5 --- /dev/null +++ b/src/admin-portal/internationalization/ToggleLanguage.js @@ -0,0 +1,40 @@ +import React from "react"; +import { useSelector } from "react-redux"; +import { getOfferedForThisCampaign } from "./AddOfferedLanguages"; + +function ToggleLanguage({ campaignId }) { + const offeredLangs = useSelector((state) => state?.campaignOfferedLanguages); + const content = getOfferedForThisCampaign(offeredLangs, campaignId); + + return ( + //
+
+
+ +
+ + Select the language you want to create content in + +
+ ); +} + +export default ToggleLanguage; diff --git a/src/api/messenger.js b/src/api/messenger.js index c91216c5..7d1866b0 100644 --- a/src/api/messenger.js +++ b/src/api/messenger.js @@ -3,6 +3,9 @@ */ import qs from "qs"; import { API_HOST } from "./urls"; +import store from "src/redux/store"; +import { PREFERRED_LANGUAGE_STORAGE_KEY } from "src/redux/redux-action-types"; +import {portalIsAdmin} from "../redux/reducers/reducers"; // import { API_HOST, IS_CANARY, IS_PROD, IS_LOCAL, CC_HOST } from '../config/constants'; export const PERMISSION_DENIED = "permission_denied"; export const SESSION_EXPIRED = "session_expired"; @@ -16,16 +19,24 @@ export const SESSION_EXPIRED = "session_expired"; * @param { String } dataToSend * @param { String } relocationPage */ -export async function apiCall (destinationUrl, dataToSend = {}, relocationPage = null) { +export async function apiCall(destinationUrl, dataToSend = {}, relocationPage = null) { + const persistedLanguage = localStorage?.getItem(PREFERRED_LANGUAGE_STORAGE_KEY) + let { activeLanguage: lang } = store?.getState() || {}; + + lang = lang || persistedLanguage; // add some meta data for context in backend const data = { // __is_prod: IS_PROD || IS_CANARY, // __is_admin_site: true, + ...(lang && !portalIsAdmin() ? { __user_language: lang } : {}), ...dataToSend, }; const formData = new FormData(); + // if (lang && !portalIsAdmin()) formData.append("__user_language", lang); Object.keys(data).map((k) => formData.append(k, data[k])); + if (lang && !portalIsAdmin()) formData.append("__user_language", lang); + if (!destinationUrl || destinationUrl.length < 2) { return { success: false, error: "Invalid URL passed to apiCall" }; @@ -52,10 +63,10 @@ export async function apiCall (destinationUrl, dataToSend = {}, relocationPage = try { const response = await fetch(destinationUrl, { - credentials: "include", - method: "POST", - body: formData, - }); + credentials: "include", + method: "POST", + body: formData, + }); const contentType = response.headers.get("content-type"); if (contentType && contentType.indexOf("application/json") !== -1) { @@ -63,10 +74,7 @@ export async function apiCall (destinationUrl, dataToSend = {}, relocationPage = if (relocationPage && json && json.success) { window.location.href = relocationPage; } else if (!json.success) { - if ( - json.error === SESSION_EXPIRED || - json.error === PERMISSION_DENIED - ) { + if (json.error === SESSION_EXPIRED || json.error === PERMISSION_DENIED) { window.location.href = "/login"; } else if (json !== "undefined") { console.log(destinationUrl, json); @@ -84,11 +92,7 @@ export async function apiCall (destinationUrl, dataToSend = {}, relocationPage = } catch (error) { const errorText = error.toString(); if (errorText.search("JSON") > -1) { - const errorMessage = - "Invalid response to " + - destinationUrl + - " Data: " + - JSON.stringify(data); + const errorMessage = "Invalid response to " + destinationUrl + " Data: " + JSON.stringify(data); // this will send message to Sentry Slack channel // Sentry.captureMessage(errorMessage); return { success: false, error: errorMessage }; @@ -100,7 +104,7 @@ export async function apiCall (destinationUrl, dataToSend = {}, relocationPage = } // ----- Used when the backend is meant to return a file for download -export async function apiCallFile (destinationUrl, dataToSend = {}) { +export async function apiCallFile(destinationUrl, dataToSend = {}) { const idToken = localStorage.getItem("idToken"); // don't need this strictUrl optional arg? Won't work with IS_LOCAL @@ -115,9 +119,7 @@ export async function apiCallFile (destinationUrl, dataToSend = {}) { // destinationUrl = "api/" + destinationUrl; // } - const url = strictUrl - ? `${API_HOST}${destinationUrl}` - : `${API_HOST}/${destinationUrl}`; + const url = strictUrl ? `${API_HOST}${destinationUrl}` : `${API_HOST}/${destinationUrl}`; // add some meta data for context in backend const data = { // __is_prod: IS_PROD, @@ -152,7 +154,7 @@ export async function apiCallFile (destinationUrl, dataToSend = {}) { const contentDisposition = response.headers.get("content-disposition"); const filename = contentDisposition ? contentDisposition.match(/filename="(.+)"/)[1] - : "download." + contentType.split("/")[1]; + : "download." + contentType?.split("/")[1]; return response.blob().then((blob) => ({ success: true, file: new File([blob], filename, { type: contentType }), diff --git a/src/assets/styles/tablet-ish.css b/src/assets/styles/tablet-ish.css new file mode 100644 index 00000000..7c118e59 --- /dev/null +++ b/src/assets/styles/tablet-ish.css @@ -0,0 +1,14 @@ +@media only screen and (min-width: 768px) and (max-width: 1460px) { + .lang-s-p-override { + right: 0px !important; + } + .lang-s-ribbon { + background: white; + /* right:0px; */ + border-top-left-radius: 50px; + border-bottom-left-radius: 50px; + padding: 10px; + box-shadow: var(--elevate-3); + padding-left: 20px; + } +} diff --git a/src/assets/styles/universal.css b/src/assets/styles/universal.css index 74b6df0d..5da7287b 100644 --- a/src/assets/styles/universal.css +++ b/src/assets/styles/universal.css @@ -1,5 +1,6 @@ @import url(./mobile-only.css); @import url(pc-only.scss); +@import url(tablet-ish.css); @import url(./elevations.css); @import url("https://fonts.googleapis.com/css?family=Google+Sans:200,300,400,400i,500,600,700,900"); @import url("https://fonts.googleapis.com/css?family=Roboto+Slab:400,700"); diff --git a/src/components/SmartRichText.js b/src/components/SmartRichText.js index dafd7fab..80ac4703 100644 --- a/src/components/SmartRichText.js +++ b/src/components/SmartRichText.js @@ -2,7 +2,17 @@ import React, { useEffect, useRef, useState } from "react"; const DEFAULT_MAX = 150; const PADDING = 15; -function SmartRichText({ text, richText, children, maxHeight = DEFAULT_MAX, style, renderSeeMore, className }) { +function SmartRichText({ + text, + richText, + children, + maxHeight = DEFAULT_MAX, + style, + renderSeeMore, + className, + seeMoreText, + seeLessText, +}) { const ref = useRef(); const [stashedHeight, setStashedHeight] = useState(0); const [displayHeight, setDisplayHeight] = useState(maxHeight); @@ -43,7 +53,7 @@ function SmartRichText({ text, richText, children, maxHeight = DEFAULT_MAX, styl }} onClick={() => toggleReadMore()} > - {inFullView ? "See Less" : "See More..."} + {inFullView ? seeLessText || "See Less..." : seeMoreText || "See More..."} ); }; diff --git a/src/components/data-table/TableFilters.js b/src/components/data-table/TableFilters.js index ca7af8bc..3c958a80 100644 --- a/src/components/data-table/TableFilters.js +++ b/src/components/data-table/TableFilters.js @@ -76,9 +76,9 @@ export function TableFilters ({ columns, originalData, first, last, setData, set */ const handleFilterByChange = (e) => { const value = e.target.value; - let [ columnId, header ] = value.split(","); + let [ columnId, header ] = value?.split(","); - let filters = columnId.split("."); + let filters = columnId?.split("."); setFilterBy(filters); if (!isEmpty(filterText)) { diff --git a/src/components/language/LanguageSelectionModal.js b/src/components/language/LanguageSelectionModal.js new file mode 100644 index 00000000..d339ef3b --- /dev/null +++ b/src/components/language/LanguageSelectionModal.js @@ -0,0 +1,43 @@ +import React from "react"; +import { useSelector } from "react-redux"; +import { getCountryFromCode } from "../../utils/utils"; + +function LanguageSelectionModal({ languages, selectLanguage }) { + const activeLanguage = useSelector((state) => state?.activeLanguage); + + return ( +
+ {languages?.map((l) => { + const isActive = l?.code === activeLanguage; + const country = getCountryFromCode(l?.code); + return ( +
selectLanguage(l?.code)} + className="touchable-opacity" + key={l?.code} + style={{ + padding: 10, + margin: 5, + borderRadius: 5, + border: "1px solid var(--app-main-color)", + cursor: "pointer", + }} + > +

{l?.name}

+
+ {l?.code} + {isActive && } + flag +
+
+ ); + })} +
+ ); +} + +export default LanguageSelectionModal; diff --git a/src/components/language/LanguageSelector.js b/src/components/language/LanguageSelector.js new file mode 100644 index 00000000..a9cc5159 --- /dev/null +++ b/src/components/language/LanguageSelector.js @@ -0,0 +1,106 @@ +import React from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { + getStaticText, + loadActiveLanguageAction, + setActiveLanguageInStorage, + toggleUniversalModal, +} from "../../redux/actions/actions"; +import { getCountryFromCode, PREFERRED_LANGUAGE_STORAGE_KEY, smartString } from "../../utils/utils"; +import LanguageSelectionModal from "./LanguageSelectionModal"; + +const DEFAULT = { name: "--", code: "---" }; +function LanguageSelector() { + const languages = useSelector((state) => state?.usersListOfLanguages); + const activeLanguage = useSelector((state) => state?.activeLanguage); + const { modals } = getStaticText(); + const staticT = modals?.languageSelectionModal || {}; + const dispatch = useDispatch(); + + const noLanguages = !languages || !languages.length || languages.length < 2; + const country = getCountryFromCode(activeLanguage); + + const openModal = (data) => { + dispatch(toggleUniversalModal(data)); + }; + + const getLangLetters = (code) => code?.split("-")[0] || ""; + const setActiveLanguage = (lang, reload = true) => { + setActiveLanguageInStorage(lang); + dispatch(loadActiveLanguageAction(lang)); + if (reload) window.location.reload(); + }; + + return ( +
+
{ + if (noLanguages) return; + openModal({ + noFooter: true, + show: true, + title: staticT?.title?.text || "Choose a Language", + component: () => , + }); + }} + className="lang-s-ribbon" + style={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }} + > + {/* */} + flag + {getLangLetters(activeLanguage)} + {!noLanguages && ( + <> + + + )} +
+
+ ); + return ( + //
+
+ +
+ ); +} + +export default LanguageSelector; diff --git a/src/components/modal/CustomModal.js b/src/components/modal/CustomModal.js index 32d2ff81..7d20c460 100644 --- a/src/components/modal/CustomModal.js +++ b/src/components/modal/CustomModal.js @@ -88,7 +88,8 @@ const SmartHeader = ({ renderHeader, close, title, imgSrc, iconName }) => { ); }; -const SmartFooter = ({ renderFooter, close }) => { +const SmartFooter = ({ renderFooter, close, noFooter }) => { + if(noFooter) return <>; if (renderFooter) return renderFooter(); return ( diff --git a/src/components/navbar/AppNavigationBar.js b/src/components/navbar/AppNavigationBar.js index 0b6a2ed5..054f61f8 100644 --- a/src/components/navbar/AppNavigationBar.js +++ b/src/components/navbar/AppNavigationBar.js @@ -3,11 +3,14 @@ import Nav from "react-bootstrap/Nav"; import Navbar from "react-bootstrap/Navbar"; import NavDropdown from "react-bootstrap/NavDropdown"; import { bindActionCreators } from "redux"; -import { connect } from "react-redux"; +import { connect, useDispatch, useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; import { addUrlSearchParams, generateUniqueRandomString } from "../../utils/utils"; -import { Col, Row } from "react-bootstrap"; +import { Button, Col, Row } from "react-bootstrap"; import * as PropTypes from "prop-types"; +import { loadActiveLanguageAction } from "../../redux/actions/actions"; +import LanguageSelector from "../language/LanguageSelector"; +import { PLATFORM } from "../../api/urls"; const EXCLUDE_FROM_NAV = ["communities"]; @@ -55,16 +58,14 @@ function AppNavigationBar({ menu, campaign }) { const navigator = useNavigate(); const { secondary_logo, primary_logo } = campaign || {}; - const home = menu?.find((m) => m?.key?.toLowerCase() === "home"); - return ( {/* MassEnergize Campaigns */} -
diff --git a/src/user-portal/pages/banner/Hero.js b/src/user-portal/pages/banner/Hero.js index bce15d9b..9ed758d8 100644 --- a/src/user-portal/pages/banner/Hero.js +++ b/src/user-portal/pages/banner/Hero.js @@ -5,13 +5,13 @@ import { useSelector } from "react-redux"; import planetB from "./../../../assets/imgs/planet-b.jpeg"; import HeroWithBelt from "./HeroWithBelt"; import CampaignNotLive from "../landing-page/CampaignNotLive"; -function Hero({ handleShareCampaign, v2 }) { +function Hero({ handleShareCampaign, v2, staticT }) { const campaign = useSelector((state) => state.campaign); const { image = {} } = campaign; if (v2) return ; return ( - + {"campaign diff --git a/src/user-portal/pages/coaches/CoachesSectionWithFilters.js b/src/user-portal/pages/coaches/CoachesSectionWithFilters.js index c7eead12..84be0706 100644 --- a/src/user-portal/pages/coaches/CoachesSectionWithFilters.js +++ b/src/user-portal/pages/coaches/CoachesSectionWithFilters.js @@ -11,7 +11,7 @@ import { ArrowButtons } from "../../../components/pieces/ArrowButtons"; import { mergeArrays } from "../../../utils/utils"; import SectionTitle from "../../../components/pieces/SectionTitle"; -function CoachesSectionWithFilters({ toggleModal, sectionId, technologies, customization }) { +function CoachesSectionWithFilters({ toggleModal, sectionId, technologies, customization, staticT }) { const containerRef = useRef(); let coaches = technologies?.map((tech) => { @@ -67,7 +67,7 @@ function CoachesSectionWithFilters({ toggleModal, sectionId, technologies, custo
- {customization?.title || " Meet the coaches"} + {customization?.title ||staticT?.title?.text || " Meet the coaches"} {/*

tech?.name} valueAccessor={(tech) => tech?.campaign_technology_id} @@ -113,7 +113,7 @@ function CoachesSectionWithFilters({ toggleModal, sectionId, technologies, custo icon: "fa-help", component: ({ close }) => , fullControl: true, - title: "Get Help", + title: staticT?.help_modal?.title || "Get Help", }) } className="touchable-opacity body-font mt-5" @@ -125,7 +125,7 @@ function CoachesSectionWithFilters({ toggleModal, sectionId, technologies, custo borderWidth: 0, }} > - Get Help + {staticT?.get_help?.text || "Get Help"}

diff --git a/src/user-portal/pages/commenting/CommentComponentForModal.js b/src/user-portal/pages/commenting/CommentComponentForModal.js index 4972e34c..72827a1f 100644 --- a/src/user-portal/pages/commenting/CommentComponentForModal.js +++ b/src/user-portal/pages/commenting/CommentComponentForModal.js @@ -5,6 +5,7 @@ import { relativeTimeAgo } from "../../../utils/utils"; import Notification from "../../../components/pieces/Notification"; import { apiCall } from "../../../api/messenger"; import CommentDeleteConfirmation from "../technology/CommentDeleteConfirmation"; +import { TRANSLATION_HELPERS_LINK } from "../footer/Footer"; function CommentComponentForModal({ comments, @@ -16,6 +17,7 @@ function CommentComponentForModal({ updateUserInRedux, commentIsForUser, onDelete, + staticT, }) { const [commentItems, setCommentItems] = useState([]); const [name, setName] = useState(""); @@ -51,7 +53,7 @@ function CommentComponentForModal({ const sendComment = () => { setError(""); - if (!comment.trim() || !name?.trim()) return setError("Please provide a name and a valid comment"); + if (!comment.trim() || !name?.trim()) return setError(staticT?.notifications?.provide_a_name?.text || "Please provide a name and a valid comment"); const doesNotHaveName = !user?.full_name; setLoading(true); @@ -81,7 +83,7 @@ function CommentComponentForModal({ user_id: user?.id || null, }).then((response) => { setLoading(false); - if (!response || !response.success) return setError(response?.error || "Sorry something happened!"); + if (!response || !response.success) return setError(response?.error || staticT?.notifications?.error?.text||"Sorry something happened!"); const latestComments = response.data; const updated = { ...(technology || {}), comments: latestComments }; setCommentItems([...latestComments].reverse()); @@ -100,13 +102,13 @@ function CommentComponentForModal({ overflowY: "scroll", minHeight: 250, maxHeight: 500, - paddingBottom: 130, + paddingBottom: 150, }} ref={comBox} > {data?.length === 0 && (
- No comments yet, be the first! + {staticT?.no_comments?.text || "No comments yet, be the first!"}
)} {data?.map((com) => { @@ -131,7 +133,8 @@ function CommentComponentForModal({ }} > - {user?.full_name || "..."} {isForCurrentUser ? " (Yours)" : ""}{" "} + {user?.full_name || "..."}{" "} + {isForCurrentUser ? (staticT?.yours?.text ? `(${staticT?.yours?.text})` : "" || " (Yours)") : ""}{" "} {" "} {community && " from "} {community} @@ -145,6 +148,7 @@ function CommentComponentForModal({ }} > onDelete && onDelete(com, (rem) => setCommentItems([...(rem || [])].reverse()))} /> @@ -179,12 +183,12 @@ function CommentComponentForModal({ >
- Your Name + {staticT?.name?.text || "Your Name"} setName(e.target.value)} type="text" - placeholder="Who is making this comment?..." + placeholder={staticT?.name_placeholder?.text || "Who is making this comment?..."} aria-label="text" aria-describedby="basic-addon1" /> @@ -195,15 +199,18 @@ function CommentComponentForModal({ setComment(e.target.value)} - placeholder="Type comment here..." + placeholder={staticT?.comment_placeholder?.text || "Type comment here..."} aria-label="User comment" aria-describedby="basic-addon2" /> + + {staticT?.help_us_translate?.text} +
diff --git a/src/user-portal/pages/events/AddToGoogleCalendar.js b/src/user-portal/pages/events/AddToGoogleCalendar.js index d5045b62..5b1a1f32 100644 --- a/src/user-portal/pages/events/AddToGoogleCalendar.js +++ b/src/user-portal/pages/events/AddToGoogleCalendar.js @@ -3,7 +3,7 @@ import { GoogleCalendar } from "datebook"; import { stateAbbreviation } from "../../../utils/utils"; -const AddToGoogleCalendar = ({ data }) => { +const AddToGoogleCalendar = ({ data, staticT }) => { const locationFormat = (location) => { if (!location) return ""; let firstLine = @@ -35,7 +35,8 @@ const AddToGoogleCalendar = ({ data }) => { style={{ background: "#ff4b00", width: "100%" }} >

- Google Calendar + {" "} + {staticT?.google_calendar?.text || "Google Calendar"}

); diff --git a/src/user-portal/pages/events/EventBox.js b/src/user-portal/pages/events/EventBox.js index b383ad5c..05db76c8 100644 --- a/src/user-portal/pages/events/EventBox.js +++ b/src/user-portal/pages/events/EventBox.js @@ -3,7 +3,22 @@ import { useNavigate } from "react-router-dom"; import { formatDate, formatTime, smartString } from "../../../utils/utils"; import { Col, Row } from "react-bootstrap"; -function EventBox({ event, campaign_technology }) { +const ONLINE = "online"; +const IN_PERSON = "in person"; +const BOTH = "both"; +export const translatedEventType = (eventType, staticT) => { + if (eventType === ONLINE) { + return staticT?.card?.online?.text || "Online"; + } + if (eventType === IN_PERSON) { + return staticT?.card?.in_person?.text || "In Person"; + } + if (eventType === BOTH) { + return staticT?.card?.in_person?.text || "In Person"; + } + return eventType; +}; +function EventBox({ event, campaign_technology, staticT }) { const { campaign } = campaign_technology || {}; const { name, image, start_date, end_date, id, event_type } = event || {}; @@ -19,7 +34,13 @@ function EventBox({ event, campaign_technology }) { {image?.url ? ( {"event"}

- {event_type} + {translatedEventType(event_type, staticT)}

diff --git a/src/user-portal/pages/events/EventsSectionWithFilters.js b/src/user-portal/pages/events/EventsSectionWithFilters.js index acefdb46..723b264f 100644 --- a/src/user-portal/pages/events/EventsSectionWithFilters.js +++ b/src/user-portal/pages/events/EventsSectionWithFilters.js @@ -9,7 +9,7 @@ import { ArrowButtons } from "../../../components/pieces/ArrowButtons"; import OurParagraph from "../../../components/OurParagraph"; import SectionTitle from "../../../components/pieces/SectionTitle"; -function EventsSectionWithFilters({ sectionId, technologies }) { +function EventsSectionWithFilters({ sectionId, technologies, staticT }) { const containerRef = useRef(); let events = technologies?.map((tech) => tech?.events); @@ -22,7 +22,7 @@ function EventsSectionWithFilters({ sectionId, technologies }) { }); else data = events; - data = sortEvents(data) + data = sortEvents(data); return ( { return ( - + ); })} @@ -58,16 +58,17 @@ function EventsSectionWithFilters({ sectionId, technologies }) {
- Events + {staticT?.title?.text || "Events"} {hasScrollableEvents && }
{hasScrollableEvents && ( - Scroll from left to right to see more events, or use the arrow buttons(top right) to scroll + {staticT?.scrollable?.text || + " Scroll from left to right to see more events, or use the arrow buttons(top right) to scroll"} )} tech?.name} valueAccessor={(tech) => tech?.campaign_technology_id} diff --git a/src/user-portal/pages/events/OneEvent.js b/src/user-portal/pages/events/OneEvent.js index 647b3ab0..fd39cf3c 100644 --- a/src/user-portal/pages/events/OneEvent.js +++ b/src/user-portal/pages/events/OneEvent.js @@ -9,12 +9,15 @@ import Loading from "../../../components/pieces/Loading"; import { bindActionCreators } from "redux"; import { connect } from "react-redux"; import { apiCall } from "../../../api/messenger"; -import { appInnitAction, updateEventsObj } from "../../../redux/actions/actions"; +import { appInnitAction, getStaticText, updateEventsObj } from "../../../redux/actions/actions"; import { formatDate, formatTime, formatTimeRange, setPageTitle } from "../../../utils/utils"; import AddToGoogleCalendar from "./AddToGoogleCalendar"; import ICSEventCreator from "./ICSEventCreator"; +import { translatedEventType } from "./EventBox"; function OneEvent({ events, updateEvents, init, campaign }) { + const { pages } = getStaticText(); + const one_event_page = pages?.one_event_page || {}; const [event, setEvent] = useState(LOADING); const [error, setError] = useState(""); const { eventId, campaign_id } = useParams(); @@ -48,7 +51,8 @@ function OneEvent({ events, updateEvents, init, campaign }) { if (!id || !event) return {error}; - if (event === LOADING) return Fetching event information...; + if (event === LOADING) + return {one_event_page?.loader?.text || "Fetching event information..."}; return ( @@ -85,20 +89,22 @@ function OneEvent({ events, updateEvents, init, campaign }) {
{event?.event_type && (
-

{event?.event_type}

+

{translatedEventType(event?.event_type, one_event_page?.sections?.card)}

)}
- Download to your calendar + {" "} + {one_event_page?.sections?.call_to_download?.text || "Download to your calendar"}
- + {" "} +
- {true && ( + {external_link && (
{ e.preventDefault(); @@ -113,7 +119,7 @@ function OneEvent({ events, updateEvents, init, campaign }) { borderRadius: 5, }} > -

Register / Join

+

{one_event_page?.sections?.call_to_register?.text || "Register / Join"}

)} diff --git a/src/user-portal/pages/footer/Footer.js b/src/user-portal/pages/footer/Footer.js index 3c40a542..3f3c3ab0 100644 --- a/src/user-portal/pages/footer/Footer.js +++ b/src/user-portal/pages/footer/Footer.js @@ -8,9 +8,13 @@ import { useMediaQuery } from "react-responsive"; import { generateUniqueRandomString } from "../../../utils/utils"; import URLS from "../../../api/urls"; import CONFIG from "../../../config/config.json"; +import { getStaticText } from "../../../redux/actions/actions"; const EXCLUDED_FOOTER_MENU_KEYS = ["deals", "vendors"]; -function Footer({ toggleModal, campaign, authUser }) { +export const TRANSLATION_HELPERS_LINK = + "https://docs.google.com/forms/d/1ay2paNG3x_gZL8hhzmghIYSEuHDA_I4MRZJf7cEt_00/viewform?edit_requested=true"; +function Footer({ toggleModal, campaign, authUser, staticT }) { + const { footer } = getStaticText(); const navigator = useNavigate(); const { navigation, newsletter_section: customization, communities } = campaign || {}; const { user } = authUser || {}; @@ -59,16 +63,24 @@ function Footer({ toggleModal, campaign, authUser }) { toggleModal({ show: true, component: (props) => ( - close && close()} /> + close && close()} + /> ), - title: `Follow ${campaign?.title}` || "...", + title: `${footer?.modal?.title?.prefix || "Follow"} ${campaign?.title}` || "...", fullControl: true, }); }; - if (isMobile) return ( - + ); return (
{/* We should be able to change this part tooo from the admin portal */}

- {customization?.title || "Newsletter"} + {customization?.title ||footer?.header?.title?.text||"Newsletter"}

- {customization?.description || "Sign up for email updates with the latest info on events and incentives!"} + {customization?.description ||footer?.header?.description?.text || "Sign up for email updates with the latest info on events and incentives!"}

{user?.email && (

{" "} - You've already subscribed with '{user?.email || ""}' + {footer?.news_letter?.subscribe_message?.text || "You've already subscribed with"}{" "} + '{user?.email || ""}'

)} + + + {footer?.help_us_translate?.text|| "Would you like to help translate this site?"} + - + + Build Version {CONFIG.BUILD_VERSION} @@ -147,7 +166,7 @@ function Footer({ toggleModal, campaign, authUser }) {

- Quick Links + {footer?.quick_links?.text || " Quick Links"}

    { }; export default connect(mapState)(Footer); -const MobileFooter = ({ signUpForNewsletter, renderMenus, customization }) => { +const MobileFooter = ({ signUpForNewsletter, renderMenus, customization, staticT }) => { return (
    { }} >
    - {customization?.title || "Newsletter"} + {customization?.title|| staticT?.header?.title?.text || "Newsletter"}

    - {customization?.description || "Sign up for email updates with the latest info on events and incentives!"} + {customization?.description ||staticT?.header?.description?.text || "Sign up for email updates with the latest info on events and incentives!"}

    - + + {staticT?.help_us_translate?.text || "Would you like to help translate this site?"} + + Build Version {CONFIG.BUILD_VERSION}
    diff --git a/src/user-portal/pages/forms/CommunitySelector.js b/src/user-portal/pages/forms/CommunitySelector.js index 15e0141c..dc984ded 100644 --- a/src/user-portal/pages/forms/CommunitySelector.js +++ b/src/user-portal/pages/forms/CommunitySelector.js @@ -3,11 +3,15 @@ import { COMMUNITY_LIST } from "../../data/user-portal-dummy-data"; import { Form, InputGroup } from "react-bootstrap"; import { connect } from "react-redux"; import { sortByProperty } from "src/utils/utils"; +import { getStaticText } from "src/redux/actions/actions"; export const OTHER = "other"; export const OTHER_JSON = { name: OTHER, id: OTHER }; function CommunitySelector({ onChange, communities, data, readOnly }) { + const { modals } = getStaticText(); + + const staticT = modals?.community_selection || {}; const [state, setState] = useState({}); data = data || {}; @@ -28,7 +32,7 @@ function CommunitySelector({ onChange, communities, data, readOnly }) { return (
    - What community do you live in? + {staticT?.selection_label?.text || "What community do you live in?"} {/*

    Please tell us where you are from (Editable)

    */}
    - {OTHER} + {staticT?.other?.text || OTHER}
    @@ -79,10 +83,12 @@ function CommunitySelector({ onChange, communities, data, readOnly }) { <>
    - Community Name + + {staticT?.form?.community_name?.text || "Community Name"} + { @@ -95,12 +101,12 @@ function CommunitySelector({ onChange, communities, data, readOnly }) {
    - Enter your zip code (Editable) + {staticT?.form?.zipcode?.label || "Enter your zip code (Editable)"} - Zip Code + {staticT?.form?.zipcode?.text || "Zip Code"} { if (!authUser) return; @@ -51,7 +54,7 @@ function GetHelpForm({ close, communities, authUser, campaign }) {
    - We will direct you to the right resources based on where you are from + {staticT?.selection_hint?.text || "We will direct you to the right resources based on where you are from"} {` ${comName}`}
    @@ -68,7 +71,7 @@ function GetHelpForm({ close, communities, authUser, campaign }) { background: "#292929", }} > - Close + {staticT?.buttons?.cancel?.text || "Close"}
    diff --git a/src/user-portal/pages/forms/JoinUsForm.js b/src/user-portal/pages/forms/JoinUsForm.js index 262297e3..55f75227 100644 --- a/src/user-portal/pages/forms/JoinUsForm.js +++ b/src/user-portal/pages/forms/JoinUsForm.js @@ -7,7 +7,7 @@ import { connect } from "react-redux"; import { validateEmail } from "../../../utils/utils"; import { apiCall } from "../../../api/messenger"; import { bindActionCreators } from "redux"; -import { loadUserObjAction } from "../../../redux/actions/actions"; +import { getStaticText, loadUserObjAction } from "../../../redux/actions/actions"; function JoinUsForm({ campaign, @@ -27,6 +27,8 @@ function JoinUsForm({ const [email, setEmail] = useState(""); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); + const { modals, toasts } = getStaticText(); + const staticT = modals?.join || {}; useState(() => { if (authUser) { @@ -48,19 +50,21 @@ function JoinUsForm({ setError({ message, good }); }; - console.log("Then lets see form", form) - const joinUs = () => { if (onConfirm) return onConfirm({ data: form, close }); // if (authUser) return alert("You've already followed. Thank you very much!"); const emailIsValid = validateEmail(email); - if (!emailIsValid) return makeNotification("Please provide a valid email address..."); + if (!emailIsValid) + return makeNotification(toasts?.join?.noEmail?.text || "Please provide a valid email address..."); const { comId, zipcode, valueForOther } = form || {}; var otherContent = {}; - if (!comId) return makeNotification("Please select a community..."); + if (!comId) return makeNotification(toasts?.join?.noCommunity?.text || "Please select a community..."); if (comId === OTHER) { otherContent = { community_name: valueForOther, zipcode, is_other: true }; - if (!zipcode || !valueForOther) return makeNotification("Please provide the zipcode & community name..."); + if (!zipcode || !valueForOther) + return makeNotification( + toasts?.join?.noZipcodeAndOther?.text || "Please provide the zipcode & community name...", + ); } setLoading(true); @@ -71,11 +75,11 @@ function JoinUsForm({ ...otherContent, }; payload = processPayload ? processPayload(payload) : payload; - makeNotification("Well done, thank you for joining us!", true); + makeNotification(toasts?.join?.joined?.text || "Well done, thank you for joining us!", true); apiCall(apiURL || "/campaigns.follow", payload).then((response) => { setLoading(false); if (!response?.success) { - makeNotification(response.error) + makeNotification(response.error); // setError("Error: ", response.error); return console.log("FOLLOW_ERROR_BE: ", response.error); } @@ -97,11 +101,11 @@ function JoinUsForm({
    {/* Join us because we are great! */} - Email + {staticT?.email?.text || "Email"} { @@ -129,7 +133,7 @@ function JoinUsForm({ background: "#292929", }} > - {cancelText || "Cancel"} + {cancelText || staticT?.buttons?.cancel?.text || "Cancel"}
    diff --git a/src/user-portal/pages/getting-started/GettingStartedSection.js b/src/user-portal/pages/getting-started/GettingStartedSection.js index e2e7c5a7..8f945ffc 100644 --- a/src/user-portal/pages/getting-started/GettingStartedSection.js +++ b/src/user-portal/pages/getting-started/GettingStartedSection.js @@ -34,6 +34,7 @@ function GettingStartedSection({ scrollToCommunities, trackActivity, authUser, + staticT, }) { const isMobile = useMediaQuery({ maxWidth: MOBILE_WIDTH }); return ( @@ -41,19 +42,24 @@ function GettingStartedSection({ - {customization?.title || "Getting Started"} + {customization?.title ||staticT?.header?.title?.text ||"Getting Started"}

    {/* This section should be edited via the admin portal */} - {customization?.description || - "Explore the actions we have under these technologies and get started right away!"} + {customization?.description || staticT?.header?.description?.text || "Explore the actions we have under these technologies and get started right away!"} {/* Explore the actions we have under these technologies and get started right away! */}

    {technologies?.map((box) => { return ( - + ); })} @@ -61,6 +67,7 @@ function GettingStartedSection({ scrollToCommunities && scrollToCommunities()} + staticT={staticT?.communities} /> @@ -73,7 +80,7 @@ function GettingStartedSection({ export default GettingStartedSection; -const DoMoreBox = ({ scrollToCommunities, isMobile }) => { +const DoMoreBox = ({ scrollToCommunities, isMobile, staticT }) => { return (
    @@ -84,10 +91,10 @@ const DoMoreBox = ({ scrollToCommunities, isMobile }) => { alt="people" />
    - Communities + {staticT?.title || "Communities"}

    - Connect with your community and check out other actions + {staticT?.description || " Connect with your community and check out other actions"}

    @@ -109,7 +116,7 @@ const DoMoreBox = ({ scrollToCommunities, isMobile }) => { style={{ background: "var(--app-main-color)" }} className="tech-btn touchable-opacity" > - Communities + {staticT?.text || "Communities"}
    diff --git a/src/user-portal/pages/getting-started/NewOneBox.js b/src/user-portal/pages/getting-started/NewOneBox.js index 54a815e5..e9e632d3 100644 --- a/src/user-portal/pages/getting-started/NewOneBox.js +++ b/src/user-portal/pages/getting-started/NewOneBox.js @@ -14,6 +14,7 @@ function NewOneBox({ campaign_id, trackActivity, authUser, + staticT, }) { const navigator = useNavigate(); const { user } = authUser || {}; @@ -29,16 +30,15 @@ function NewOneBox({ return (
    - {image && {image?.name}} + {image && ( + {image?.name} + )}
    {name}
    -

    - {smartString(summary, 75) || "..."} -

    +

    {smartString(summary, 75) || "..."}

    -
    @@ -53,7 +53,7 @@ function NewOneBox({ }} style={{ fontWeight: "bold", color: "var(--app-accent-3)" }} > - Learn More... + {staticT?.learn_more?.text || " Learn More..."} @@ -65,9 +65,9 @@ function NewOneBox({ navigator(`${route}?section=vendors`); }} className="tech-btn touchable-opacity" - style={{ background: "var(--app-accent-3)",}} + style={{ background: "var(--app-accent-3)" }} > - Quote + {staticT?.quote?.text || "Quote"} @@ -79,7 +79,7 @@ function NewOneBox({ style={{ background: "var(--app-main-color)" }} className="tech-btn touchable-opacity" > - Coach + {staticT?.coach?.text || "Coach"} diff --git a/src/user-portal/pages/getting-started/OneBox.js b/src/user-portal/pages/getting-started/OneBox.js index e99703a6..958b27fd 100644 --- a/src/user-portal/pages/getting-started/OneBox.js +++ b/src/user-portal/pages/getting-started/OneBox.js @@ -8,8 +8,19 @@ import { smartString } from "../../../utils/utils"; import NewOneBox from "./NewOneBox"; function OneBox(props) { - const { icon, v2, name, campaign_technology_id, isIcon, image, summary, campaign_id, trackActivity, authUser } = - props; + const { + icon, + v2, + name, + campaign_technology_id, + isIcon, + image, + summary, + campaign_id, + trackActivity, + authUser, + staticT, + } = props; const navigator = useNavigate(); const { user } = authUser || {}; @@ -22,7 +33,7 @@ function OneBox(props) { email: user?.email, }; - return ; + return ; /*return (
    diff --git a/src/user-portal/pages/landing-page/CampaignNotLive.js b/src/user-portal/pages/landing-page/CampaignNotLive.js index fdd7efd8..3995b232 100644 --- a/src/user-portal/pages/landing-page/CampaignNotLive.js +++ b/src/user-portal/pages/landing-page/CampaignNotLive.js @@ -1,27 +1,23 @@ -import React, {useState} from "react"; -import {Alert, Button, Col, Row} from "react-bootstrap"; -import {connect} from "react-redux"; +import React, { useState } from "react"; +import { Alert, Button, Col, Row } from "react-bootstrap"; +import { connect } from "react-redux"; +import { getStaticText } from "../../../redux/actions/actions"; -function CampaignNotLive({campaign}) { +function CampaignNotLive({ campaign }) { const [show, setShow] = useState(true); - + const { inPreview: staticT } = getStaticText(); if (campaign?.is_published || !show) return <>; return (

    - - {`This campaign '${ - campaign?.title || "..." - }' is not published yet. Admins are still working on it. Please come back later when its complete...`} + + {`'${campaign?.title || "..."}' ${staticT?.notice?.text}`}

    - +
    @@ -29,6 +25,6 @@ function CampaignNotLive({campaign}) { } const mapState = (state) => { - return {campaign: state.campaign}; + return { campaign: state.campaign }; }; export default connect(mapState)(CampaignNotLive); diff --git a/src/user-portal/pages/landing-page/DoMore.js b/src/user-portal/pages/landing-page/DoMore.js index 82b7ce21..4506483d 100644 --- a/src/user-portal/pages/landing-page/DoMore.js +++ b/src/user-portal/pages/landing-page/DoMore.js @@ -4,6 +4,7 @@ import { Col, Container, Row } from "react-bootstrap"; import SectionTitle from "../../../components/pieces/SectionTitle"; import { useSelector } from "react-redux"; import OptimumWrapper from "../wrappers/OptimumWrapper"; +import {BASE_URL, getCommunityPortalBaseURL} from "../../../utils/utils"; const DoMore = (props) => { const { optimum } = props; @@ -18,19 +19,20 @@ const DoMore = (props) => { ); }; -function DoMoreComponent() { +function DoMoreComponent({ staticT }) { const campaign = useSelector((state) => state.campaign); const { communities, communities_section } = campaign || {}; const { title, description } = communities_section || {}; - const COMMUNITY_PORTAL_URL = "https://communities.massenergize.org/"; return (
    {/* */} - {title || " Participating Communities"} + + {title || staticT?.title?.text || "..."} + {/*

    { - window.open(`${COMMUNITY_PORTAL_URL}${community?.subdomain}`); + window.open(`${BASE_URL}${community?.subdomain}`); }} className="touchable-opacity body-font" style={{ diff --git a/src/user-portal/pages/landing-page/LandingPage.js b/src/user-portal/pages/landing-page/LandingPage.js index 36e4fdfe..b46be10a 100644 --- a/src/user-portal/pages/landing-page/LandingPage.js +++ b/src/user-portal/pages/landing-page/LandingPage.js @@ -9,7 +9,13 @@ import GettingStartedSection from "../getting-started/GettingStartedSection"; import { connect, useSelector } from "react-redux"; import { useParams } from "react-router-dom"; import { bindActionCreators } from "redux"; -import { USER_STORAGE_KEY, appInnitAction, loadUserObjAction, trackActivity } from "../../../redux/actions/actions"; +import { + USER_STORAGE_KEY, + appInnitAction, + getStaticText, + loadUserObjAction, + trackActivity, +} from "../../../redux/actions/actions"; import { LOADING, MOBILE_WIDTH } from "../../../utils/Constants"; import Loading from "../../../components/pieces/Loading"; import NotFound from "../error/404"; @@ -24,6 +30,7 @@ import CoachesSectionWithFilters from "../coaches/CoachesSectionWithFilters"; import ShareBox from "../sharing/ShareBox"; import Hero from "../banner/Hero"; import { useMediaQuery } from "react-responsive"; +import BlanketNotification from "../../../components/pieces/BlanketNotification"; function LandingPage({ toggleModal, @@ -38,6 +45,9 @@ function LandingPage({ }) { const [mounted, setMounted] = useState(false); const isMobile = useMediaQuery({ maxWidth: MOBILE_WIDTH }); + const { loader, pages, share: shareStaticT, inPreview: previewStaticT } = getStaticText(); + const homepageStaticT = pages?.homepage || {}; + const blanketNotification = useSelector((state) => state.blanketNotification); const coachesRef = useRef(); const eventsRef = useRef(); @@ -59,8 +69,8 @@ function LandingPage({ const salt = fetchUrlParams("salt"); const heroAB = fetchUrlParams("hero"); const showHeroV1 = heroAB && heroAB === "v2"; - - const { key_contact, is_published, description, technologies_section, coaches_section, about_us_title } = campaign || {}; + const { key_contact, is_published, description, technologies_section, coaches_section, about_us_title } = + campaign || {}; const technologies = campaign?.technologies || []; const { campaignId } = useParams(); @@ -105,18 +115,20 @@ function LandingPage({ }; const tellUsWhereYouAreFrom = (justLoadedCampaign) => { + if (blanketNotification) return; const user = authUser || localStorage.getItem(USER_STORAGE_KEY); const firstTime = !user || user === "null"; + const { modals } = getStaticText(); if (!firstTime) return; toggleModal({ show: true, - title: `Please tell us where you are from`, + title: modals?.whereFrom?.title?.text || `Please tell us where you are from`, component: ({ close }) => ( stashUserCommunity({ ...props, campaign: justLoadedCampaign })} /> @@ -129,7 +141,7 @@ function LandingPage({ const data = { description }; toggleModal({ show: true, - title: `About ${campaign?.title || ""}`, + title: `${campaign?.title || ""}`, component: ({ close }) => , fullControl: true, }); @@ -138,9 +150,9 @@ function LandingPage({ const handleShareCampaign = () => { toggleModal({ show: true, - title: "Share Campaign", + title: homepageStaticT?.share?.title?.text || "Share Campaign", // iconName: "fa-comment", - component: () => , + component: () => , modalNativeProps: { size: "lg" }, fullControl: true, }); @@ -150,7 +162,8 @@ function LandingPage({ if (!preview) init(campaignId); }, []); - if (campaign === LOADING && !preview) return Fetching campaign details...; + if (campaign === LOADING && !preview) + return {loader?.text || "Fetching campaign details..."}; if (!campaign) return ; @@ -192,19 +205,26 @@ function LandingPage({ borderBottomLeftRadius: 55, }} > - PREVIEW MODE + {previewStaticT?.button?.text || "PREVIEW MODE"}

    )} - + @@ -216,6 +236,7 @@ function LandingPage({ sectionId="getting-started-section" trackActivity={trackActivity} authUser={authUser} + staticT={homepageStaticT?.sections?.getting_started_section} />

    @@ -226,12 +247,17 @@ function LandingPage({ technologies={technologies} sectionId="testimonial-section" protectedFunction={(options) => triggerProtectedFunctionality(authUser, options)} + staticT={homepageStaticT?.sections?.testimonials_section} />
    {/*
    */}
    - +
    @@ -240,6 +266,7 @@ function LandingPage({ technologies={technologies} toggleModal={toggleModal} sectionId="coaches-section" + staticT={homepageStaticT?.sections?.coaches_section} />
    diff --git a/src/user-portal/pages/landing-page/RoamingBox.js b/src/user-portal/pages/landing-page/RoamingBox.js index 507886ae..9a77beb3 100644 --- a/src/user-portal/pages/landing-page/RoamingBox.js +++ b/src/user-portal/pages/landing-page/RoamingBox.js @@ -3,7 +3,7 @@ import { Button, Col, Container, Row } from "react-bootstrap"; import phone_call from "./../../../assets/imgs/phone_call.png"; import SmartRichText from "../../../components/SmartRichText"; const LEN = 250; -function RoamingBox({ advert, keyContact, showMore }) { +function RoamingBox({ advert, keyContact, showMore, staticT }) { const desc = advert?.description; const preview = desc?.substr(0, LEN); const isLong = desc?.length > LEN; @@ -13,47 +13,49 @@ function RoamingBox({ advert, keyContact, showMore }) {
    {/**/} - - -
    + +
    -
    -

    {advert?.title}

    -
    -
    +
    +

    {advert?.title}

    +
    +
    + {/*
    */} + { + if (!isLong) return <>; + return ( + + ); }} > - {/*
    */} - { - if (!isLong) return <>; - return ( - - ); - }} - > - {advert?.description} - + {advert?.description} + - - {/*
    */} - {/*

    + {/*

    */} + {/*

    {preview} {isLong ? "..." : ""}

    */} - {/*
    */} -
    - +
    + - + {keyContact?.image?.name + {staticT?.keyContact?.text || "Key Contact"} +
    - {keyContact?.name || "..."} +
    + {email && ( +

    { + window.open(`mailto:${keyContact?.email}`, "_blank"); }} - alt={keyContact?.image?.name || "Key Contact"} - > - Key Contact -

    - {keyContact?.name || "..."} -
    - {email && ( -

    { - window.open(`mailto:${keyContact?.email}`, "_blank"); - }} - className="mb-1 touchable-opacity small-font" - style={{ - display: "flex", - flexDirection: "row", - textDecoration: "underline", - alignItems: "center", - fontWeight: "bold", - }} - > - - {keyContact?.email} -

    - )} - {phone_number && ( -

    { - window.open(`tel:${keyContact?.phone_number}`, "_blank"); - }} - style={{ textDecoration: "underline" }} - > - {keyContact?.phone_number} -

    - )} - - + + {keyContact?.email} +

    + )} + {phone_number && ( +

    { + window.open(`tel:${keyContact?.phone_number}`, "_blank"); + }} + style={{ textDecoration: "underline" }} + > + {keyContact?.phone_number} +

    + )} + + {/**/}
    diff --git a/src/user-portal/pages/sharing/ShareBox.js b/src/user-portal/pages/sharing/ShareBox.js index c0d81611..2cac14f3 100644 --- a/src/user-portal/pages/sharing/ShareBox.js +++ b/src/user-portal/pages/sharing/ShareBox.js @@ -3,20 +3,23 @@ import { Button, Form, InputGroup } from "react-bootstrap"; import { apiCall } from "../../../api/messenger"; import copy from "clipboard-copy"; import URLS, { API_HOST } from "../../../api/urls"; -const items = [ +let items = [ { key: "facebook", text: "acebook", icon: "fa-facebook", alias: "facebook" }, { key: "twitter", text: "Twitter", icon: "fa-twitter", alias: "twitter" }, { key: "email", text: "Email", icon: "fa-envelope", alias: "email" }, { key: "whatsapp", text: "Whatsapp", icon: "fa-whatsapp", alias: "whatsapp" }, { key: "other", text: "Other", icon: "", alias: "" }, ]; -function ShareBox({ data, onChange, campaign, authUser }) { +function ShareBox({ data, onChange, campaign, authUser, staticT }) { const [copied, setCopied] = useState(false); const [state, setState] = useState({}); const [error, setError] = useState({}); const [link, setLink] = useState(""); data = data || {}; + const rem = items.filter((item) => item.key !== "other"); + items = [...rem, { key: "other", text: staticT?.other?.text || "Other", icon: "", alias: "" }]; + const { user } = authUser || {}; const generateLink = (obj) => { const path = new URL(window.location.href); @@ -68,7 +71,7 @@ function ShareBox({ data, onChange, campaign, authUser }) {
    - Please select a platform that you would like to share this technology to + {staticT?.instruction?.text || "Please select a platform that you would like to share this technology to"}
    - {copied ? "Copied!" : "Copy Link"} + {copied ? staticT?.button?.copied?.text || "Copied!" : staticT?.button?.copy?.text || "Copy Link"} - You can copy the link and share it {state?.platform === "other" ? "" : ` on ${state.platform}`} + {staticT?.hint?.text || "You can copy the link and share it"}{" "} + {state?.platform === "other" ? "" : ` on ${state.platform}`}
    diff --git a/src/user-portal/pages/technology/CommentDeleteConfirmation.js b/src/user-portal/pages/technology/CommentDeleteConfirmation.js index 9fc42adc..4a51c65e 100644 --- a/src/user-portal/pages/technology/CommentDeleteConfirmation.js +++ b/src/user-portal/pages/technology/CommentDeleteConfirmation.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -function CommentDeleteConfirmation ({ onDelete, show }) { +function CommentDeleteConfirmation({ onDelete, show, staticT }) { const [isDeleting, setIsDeleting] = useState(false); if (!show) return <>; @@ -13,20 +13,16 @@ function CommentDeleteConfirmation ({ onDelete, show }) { if (isDeleting) return ( - Are you sure? - onDelete && onDelete()} - className="touchable-opacity" - style={{ ...common }} - > - YES + {staticT?.deletion?.title?.text || "Are you sure?"} + onDelete && onDelete()} className="touchable-opacity" style={{ ...common }}> + {staticT?.deletion?.buttons?.yes?.text || "YES"} setIsDeleting(false)} style={{ ...common, color: "#a52424" }} > - NO + {staticT?.deletion?.buttons?.no?.text || "NO"} ); @@ -42,7 +38,7 @@ function CommentDeleteConfirmation ({ onDelete, show }) { fontWeight: "bold", }} > - Delete{" "} + {staticT?.deletion?.delete?.text || "Delete"} ); } diff --git a/src/user-portal/pages/technology/InteractionsPanel.js b/src/user-portal/pages/technology/InteractionsPanel.js index bf6bc8a2..4862146a 100644 --- a/src/user-portal/pages/technology/InteractionsPanel.js +++ b/src/user-portal/pages/technology/InteractionsPanel.js @@ -7,7 +7,7 @@ const interactions = [ { icon: "fa-eye", name: "Views", count: 3542 }, // { icon: "fa-share", name: "Shares", count: 15 }, ]; -function InteractionsPanel({ openCommentBox, likes, views, comments, openShareBox, like, liked }) { +function InteractionsPanel({ openCommentBox, likes, views, comments, openShareBox, like, liked, staticT }) { const [hasLiked, setHasLiked] = useState(false); const [likeCount, setLikeCount] = useState(0); @@ -43,7 +43,9 @@ function InteractionsPanel({ openCommentBox, likes, views, comments, openShareBo {`${likeCount ? likeCount : ""} `} - {`${!likeCount || likeCount === 1 ? " Like" : " Likes"}`} + {`${ + !likeCount || likeCount === 1 ? staticT?.like?.text || " Like" : staticT?.like?.plural || " Likes" + }`}
    {`${comments ? comments : ""}`} - {`${!comments || comments === 1 ? " Comment" : " Comments"}`} + {`${ + !comments || comments === 1 ? staticT?.comment?.text || " Comment" : staticT?.comment?.plural || " Comments" + }`}
    {views ? ( @@ -75,7 +79,10 @@ function InteractionsPanel({ openCommentBox, likes, views, comments, openShareBo > - {views} {views === 1 ? " View" : "Views"} + {views}{" "} + + {views === 1 ? staticT?.view?.text || " View" : staticT?.view?.plural || "Views"} +
    ) : ( @@ -93,7 +100,9 @@ function InteractionsPanel({ openCommentBox, likes, views, comments, openShareBo onClick={() => openShareBox()} > - Share + + {staticT?.share?.text || "Share"} +
    {/* {interactions.map((inter, index) => { diff --git a/src/user-portal/pages/technology/OneTechEventSection.js b/src/user-portal/pages/technology/OneTechEventSection.js index bca0c0ab..fd0c057a 100644 --- a/src/user-portal/pages/technology/OneTechEventSection.js +++ b/src/user-portal/pages/technology/OneTechEventSection.js @@ -6,7 +6,7 @@ import TestimonialBox from "../testimonials/TestimonialBox"; import EventBox from "../events/EventBox"; import { ArrowButtons } from "../../../components/pieces/ArrowButtons"; -function OneTechEventSection({ events, style, wrapperStyle }) { +function OneTechEventSection({ events, style, wrapperStyle, staticT }) { const containerRef = useRef(); if (!events?.length) return <>; @@ -30,7 +30,7 @@ function OneTechEventSection({ events, style, wrapperStyle }) { // justifyContent: "center", }} > - Events + {staticT?.title?.text || "..."}
    @@ -48,7 +48,7 @@ function OneTechEventSection({ events, style, wrapperStyle }) { {events.map((item, index) => { return ( - + ); })} diff --git a/src/user-portal/pages/technology/OneTechMeetTheCoachesSetion.js b/src/user-portal/pages/technology/OneTechMeetTheCoachesSetion.js index e655af43..5831500f 100644 --- a/src/user-portal/pages/technology/OneTechMeetTheCoachesSetion.js +++ b/src/user-portal/pages/technology/OneTechMeetTheCoachesSetion.js @@ -5,34 +5,8 @@ import { Button, Col, Row } from "react-bootstrap"; import OneCoach from "../coaches/OneCoach"; import { ArrowButtons } from "../../../components/pieces/ArrowButtons"; -const SCROLL_TRAVEL = 100; -const dummies = [ - { - icon: "fa-globe", - title: "ENVIRONMENTALLY FRIENDLY", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, - { - icon: "fa-lightbulb-o", - title: "ECONOMIC BENEFITS ", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, - { - icon: "fa-fire", - title: "HEALTH & WELLNESS", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, - { - icon: "fa-cog", - title: "COMFORT", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, -]; -function OneTechMeetTheCoachesSection({ toggleModal, sectionId, coaches, data, ref }) { + +function OneTechMeetTheCoachesSection({ toggleModal, sectionId, coaches, data, ref, staticT }) { const { title, description } = data || {}; const scrollContainerRef = useRef(null); @@ -49,7 +23,7 @@ function OneTechMeetTheCoachesSection({ toggleModal, sectionId, coaches, data, r
    - {title || "Meet the Coaches"} + {title || staticT?.title?.text || " Meet the coaches"}
    @@ -94,7 +68,7 @@ function OneTechMeetTheCoachesSection({ toggleModal, sectionId, coaches, data, r }} onClick={() => toggleModal()} > - Get Help{" "} + {staticT?.button?.text || "Get Help"}
    diff --git a/src/user-portal/pages/technology/OneTechTestimonialsSection.js b/src/user-portal/pages/technology/OneTechTestimonialsSection.js index 58aca501..34e00210 100644 --- a/src/user-portal/pages/technology/OneTechTestimonialsSection.js +++ b/src/user-portal/pages/technology/OneTechTestimonialsSection.js @@ -9,34 +9,7 @@ import { useNavigate } from "react-router-dom"; import { TESTIMONIAL_FORM_SHOW_KEY } from "../testimonials/TestimonialSectionWithFilters"; import { addUrlSearchParams } from "../../../utils/utils"; -const dummies = [ - { - icon: "fa-globe", - title: "ENVIRONMENTALLY FRIENDLY", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, - { - icon: "fa-lightbulb-o", - title: "ECONOMIC BENEFITS ", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, - { - icon: "fa-fire", - title: "HEALTH & WELLNESS", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, - { - icon: "fa-cog", - title: "COMFORT", - content: - "1500s, when an unknown printer took a galley of type rised in the 1960s with the release of L1500s, when an unknown printer took a galley of type rised in the 1960s with the release of ", - }, -]; - -function OneTechTestimonialsSection({ sectionId, testimonials, campaign, links }) { +function OneTechTestimonialsSection({ sectionId, testimonials, campaign, links, staticT }) { const containerRef = useRef(); const navigator = useNavigate(); @@ -69,7 +42,7 @@ function OneTechTestimonialsSection({ sectionId, testimonials, campaign, links } // justifyContent: "center", }} > - Testimonials + {staticT?.title?.text || "Testimonials"} {hasScrollableTestimonials && (
    @@ -78,12 +51,15 @@ function OneTechTestimonialsSection({ sectionId, testimonials, campaign, links } )}
    - navigator(testimonialRoute)} /> + navigator(testimonialRoute)} + text={staticT?.call_to_add_testimonial?.text || ""} + /> {testimonials.map((item, index) => { return ( - + ); })} diff --git a/src/user-portal/pages/technology/TakeActionSection.js b/src/user-portal/pages/technology/TakeActionSection.js index 922f5194..b540c6eb 100644 --- a/src/user-portal/pages/technology/TakeActionSection.js +++ b/src/user-portal/pages/technology/TakeActionSection.js @@ -7,6 +7,7 @@ import { FULL_TECHNOLOGY } from "../../../utils/Constants"; const dummies = [ { + key: "coaches", icon: "fa-question", title: "Ask A Question ", actionText: "Get Help", @@ -15,6 +16,7 @@ const dummies = [ type: "help", }, { + key: "incentives", icon: "fa-money", title: "Show Me The Money", actionText: "Incentives", @@ -23,6 +25,7 @@ const dummies = [ type: "incentives", }, { + key: "vendors", icon: "fa-handshake-o", title: "Find A Vendor", actionText: "Vendors", @@ -31,7 +34,7 @@ const dummies = [ type: "vendors", }, ]; -function TakeAtionSetion({ sectionId, scrollToSection, authUser, trackActivity, campaign, vendors }) { +function TakeAtionSetion({ sectionId, scrollToSection, authUser, trackActivity, campaign, vendors, staticT }) { const navigator = useNavigate(); const { user } = authUser || {}; const common = { @@ -56,10 +59,11 @@ function TakeAtionSetion({ sectionId, scrollToSection, authUser, trackActivity, > - Take Action! + {staticT?.title?.text || "Take Action"} {dummies.map((item, index) => { + const translatedT = staticT?.[item?.key] || {}; return (
    - {item.title} + {translatedT?.title || item.title}

    - {item.content} + {translatedT?.description || item.content}

    - {item.actionText} + {translatedT?.button?.text || item.actionText}

    diff --git a/src/user-portal/pages/technology/TechnologyFullViewPage.js b/src/user-portal/pages/technology/TechnologyFullViewPage.js index f508981f..cfdd288c 100644 --- a/src/user-portal/pages/technology/TechnologyFullViewPage.js +++ b/src/user-portal/pages/technology/TechnologyFullViewPage.js @@ -27,6 +27,7 @@ import { apiCall } from "../../../api/messenger"; import Loading from "../../../components/pieces/Loading"; import { appInnitAction, + getStaticText, loadUserObjAction, setCommentsAction, trackActivity, @@ -63,6 +64,10 @@ function TechnologyFullViewPage({ const authUser = user; // const hasUser = authUser?.user; const [mounted, setMounted] = useState(false); + const { pages } = getStaticText(); + const one_technology_page = pages?.one_technology_page || {}; + const { sections, loader, share: shareStaticT } = one_technology_page; + const modalStaticT = sections?.comments?.modal || {}; // const [idsToRefMap, setidsToRefMap] = useState({}); const coachesRef = useRef(); const vendorsRef = useRef(); @@ -159,7 +164,7 @@ function TechnologyFullViewPage({ if (!id || !technology) return {error}; - if (technology === LOADING) return Fetching technology information...; + if (technology === LOADING) return {loader?.text || "Fetching technology information..."}; const { name, @@ -199,20 +204,21 @@ function TechnologyFullViewPage({ updateTechList(response?.data, id); }); }; - + // console.log("==STATicT==", options); // NB: Dont worry, I will merge the two trigger fxns into one, when there is more time - const triggerRegistration = () => { + const triggerRegistration = (options) => { + const { staticT } = options || {}; toggleModal({ show: true, - title: `Before you continue, we would like to know you`, + title: staticT?.preComment?.title?.text || `Before you continue, we would like to know you`, iconName: "fa-comment", component: ({ close }) => ( { close && close(); - triggerCommentBox(user); + triggerCommentBox(user, { staticT }); }} /> ), @@ -221,12 +227,12 @@ function TechnologyFullViewPage({ }); }; - const triggerCommentBox = (userObject) => { + const triggerCommentBox = (userObject, options) => { const { user } = userObject || {}; - if (!user) return triggerRegistration(); + if (!user) return triggerRegistration(options); toggleModal({ show: true, - title: "Read comments or add yours", + title: sections?.comments?.title?.text || "Read comments or add yours", iconName: "fa-comment", component: () => ( { // setTechnology(data); updateTechList(data, id); @@ -252,9 +259,9 @@ function TechnologyFullViewPage({ const openShareBox = () => { toggleModal({ show: true, - title: "Share", + title: shareStaticT?.title?.text || "Share", // iconName: "fa-comment", - component: () => , + component: () => , modalNativeProps: { size: "lg" }, fullControl: true, }); @@ -311,22 +318,29 @@ function TechnologyFullViewPage({ {image?.url && {"event"}} triggerCommentBox(authUser)} + openCommentBox={() => triggerCommentBox(authUser, { staticT: modalStaticT })} liked={technology?.has_liked} likes={likes} like={() => like(authUser?.user)} views={campaign_technology_views} comments={comments?.length || 0} + staticT={sections?.interactions} /> - {description} + + {description} +
    - {`Get updates on ${technology?.name || "..."}`} + {`${sections?.get_updates?.title?.text || "Get updates on"} ${technology?.name || "..."}`}

    toggleModal({ show: true, - title: `Get updates on ${technology?.name || "..."}`, + title: `${sections?.get_updates?.title?.text || "Get updates on"} ${technology?.name || "..."}`, component: ({ close }) => ( { const data = { @@ -395,7 +410,7 @@ function TechnologyFullViewPage({ fontWeight: "bold", }} > - Get Updates! + {sections?.get_updates?.button?.text || "Get Updates!"}

    @@ -425,7 +440,7 @@ function TechnologyFullViewPage({ fontWeight: "bold", }} > - Comments {comments?.length ? `(${comments?.length})` : ""} + {sections?.comments?.title?.text || "Comments"} {comments?.length ? `(${comments?.length})` : ""}

@@ -458,7 +473,12 @@ function TechnologyFullViewPage({ color: !isForCurrentUser ? "var(--app-main-color)" : "var(--app-accent-3)", }} > - {user?.full_name || "..."} {isForCurrentUser ? " (Yours)" : ""} + {user?.full_name || "..."}{" "} + {isForCurrentUser + ? modalStaticT?.yours?.text + ? `(${modalStaticT?.yours?.text})` + : "" || " (Yours)" + : ""} {message.substr(0, COMMENT_LENGTH)} @@ -471,9 +491,9 @@ function TechnologyFullViewPage({ color: "var(--app-accent-3)", fontWeight: "bold", }} - onClick={() => triggerCommentBox(authUser)} + onClick={() => triggerCommentBox(authUser, { staticT: modalStaticT })} > - See more... + {sections?.comments?.see_more_trunc?.text || "See more..."} ) : ( <> @@ -487,7 +507,11 @@ function TechnologyFullViewPage({ flexDirection: "row", }} > - deleteComment(com)} /> + deleteComment(com)} + /> triggerCommentBox(authUser)} + onClick={() => triggerCommentBox(authUser, { staticT: modalStaticT })} >

- See More Comments + {sections?.comments?.see_more?.text || "See More Comments"}

triggerCommentBox(authUser)} + onClick={() => triggerCommentBox(authUser, { staticT: modalStaticT })} > -

Add a Comment

+

+ {sections?.comments?.call_to_add?.text || "Add a Comment"} +

@@ -553,6 +579,7 @@ function TechnologyFullViewPage({ overview={overview} campaignName={name} overview_title={technology?.overview_title} + staticT={sections?.why_section} />
@@ -570,10 +598,12 @@ function TechnologyFullViewPage({ sectionId="testimonial-section" campaign={campaign} links={navigation} + staticT={sections?.testimonials_section} />
, fullControl: true, - title: "Get Help", + title: sections?.coaches_section?.button?.text || "Get Help", }) } /> @@ -610,16 +640,26 @@ function TechnologyFullViewPage({ />
- +
- +
- +