diff --git a/micro-frontends/sample/package.json b/micro-frontends/sample/package.json index 85675b1bfcd..1a03f96ad1e 100644 --- a/micro-frontends/sample/package.json +++ b/micro-frontends/sample/package.json @@ -10,10 +10,12 @@ "preview": "vite preview" }, "dependencies": { + "@tanstack/react-query": "5.50.1", + "axios": "1.7.2", "i18next": "23.11.5", - "axios":"1.7.2", "i18next-http-backend": "2.5.2", "idb": "8.0.0", + "localforage": "^1.10.0", "react": "19.0.0-beta-26f2496093-20240514", "react-dom": "19.0.0-beta-26f2496093-20240514", "react-hook-form": "7.52.0", diff --git a/micro-frontends/sample/src/App.jsx b/micro-frontends/sample/src/App.jsx index a22be439a5e..a8f3255d11e 100644 --- a/micro-frontends/sample/src/App.jsx +++ b/micro-frontends/sample/src/App.jsx @@ -15,37 +15,65 @@ import LanguageSwitcher from "./components/LanguageSelector"; import MyComponent from "./components/SampleComponent"; // import TestIdb from "./pages/TestIdb.jsx"; // import { isOnline, onNetworkChange } from './networkStatus'; - +import { useQuery } from "@tanstack/react-query"; import { Suspense, lazy } from "react"; import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom"; +import withAuth from "./hoc/withAuth"; const routes = [ { url: "complex-screen", + withAuth: true, component: lazy(() => import("./pages/test")), }, { url: "tab-form", + withAuth: true, component: lazy(() => import("./pages/TabForm")), }, { url: "TestIdb", + withAuth: true, component: lazy(() => import("./pages/testIdb")), }, { url: "sample", + withAuth: true, component: lazy(() => import("./pages/Sample")), }, { url: "stepper-form", + withAuth: false, component: lazy(() => import("./pages/StepperForm")), }, ]; +const fetchTodo = async () => { + const response = await fetch("https://jsonplaceholder.typicode.com/todos/1"); + return response.json(); +}; + +export const TodoComponent = () => { + const { data, error, isLoading } = useQuery({ + queryKey: ["todo"], + queryFn: fetchTodo, + refetchInterval: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchIntervalInBackground: false, + }); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error.message}
; + + return
{data.title}
; +}; function App() { return ( <>
+ Loading... @@ -72,7 +100,9 @@ function App() { Loading...
}> {routes?.map((route) => { - const Component = route?.component; + const Component = route?.withAuth + ? withAuth(route?.component) + : route?.component; return ( } /> ); diff --git a/micro-frontends/sample/src/hoc/withAuth.jsx b/micro-frontends/sample/src/hoc/withAuth.jsx new file mode 100644 index 00000000000..03fa9e33484 --- /dev/null +++ b/micro-frontends/sample/src/hoc/withAuth.jsx @@ -0,0 +1,23 @@ +import React, { useLayoutEffect } from "react"; +import withNavigator from "./withNavigator"; +import { useAuthState } from "../state/useAuthState"; + +const withAuth = (WrappedComponent) => { + const { data } = useAuthState(); + return (props) => { + useLayoutEffect(() => { + if (data?.isSignedIn) { + console.log("user is signed in"); + } else { + console.log("user is not signedin"); + } + return () => { + console.log("Component unmounted"); + }; + }, []); + const WrappedComp = withNavigator(WrappedComponent); + return ; + }; +}; + +export default withAuth; diff --git a/micro-frontends/sample/src/hoc/withNavigator.jsx b/micro-frontends/sample/src/hoc/withNavigator.jsx new file mode 100644 index 00000000000..ffa4ef657ea --- /dev/null +++ b/micro-frontends/sample/src/hoc/withNavigator.jsx @@ -0,0 +1,28 @@ +import React, { useLayoutEffect } from "react"; +import { useNavigatorState } from "../state/useNavigatorState"; + +const withNavigator = (WrappedComponent) => { + const { setData, data } = useNavigatorState(); + + return (props) => { + useLayoutEffect(() => { + data?.currentScreen != window.location.pathname && + setData({ + ...data, + currentScreen: window.location.pathname, + history: data?.history + ? [...data?.history, window.location.pathname] + : [window.location.pathname], + previousScreen: data?.currentScreen, + }); + + return () => { + console.log("Component unmounted withNavigator"); + }; + }, []); + + return ; + }; +}; + +export default withNavigator; diff --git a/micro-frontends/sample/src/main.jsx b/micro-frontends/sample/src/main.jsx index 8f7d48b8216..c2a38783139 100644 --- a/micro-frontends/sample/src/main.jsx +++ b/micro-frontends/sample/src/main.jsx @@ -1,17 +1,23 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.jsx' -import './index.css' -import { I18nextProvider } from 'react-i18next'; -import i18n from './configs/i18.js'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; +import "./index.css"; +import { I18nextProvider } from "react-i18next"; +import i18n from "./configs/i18.js"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { hydrateAllQueries, queryClient } from "./state/stateConfigs"; +const initializeApp = async () => { + await hydrateAllQueries(); + ReactDOM.createRoot(document.getElementById("root")).render( + + + + + + + + ); +}; - - -ReactDOM.createRoot(document.getElementById('root')).render( - - - - - , -) +initializeApp(); diff --git a/micro-frontends/sample/src/pages/TabForm.jsx b/micro-frontends/sample/src/pages/TabForm.jsx index 5cdfe2c9090..c34074f9778 100644 --- a/micro-frontends/sample/src/pages/TabForm.jsx +++ b/micro-frontends/sample/src/pages/TabForm.jsx @@ -1,15 +1,23 @@ -import React, { useState } from 'react'; -import { useForm, Controller, useFieldArray } from 'react-hook-form'; -import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; -import 'react-tabs/style/react-tabs.css'; +import React, { useState } from "react"; +import { useForm, Controller, useFieldArray } from "react-hook-form"; +import { Tab, Tabs, TabList, TabPanel } from "react-tabs"; +import "react-tabs/style/react-tabs.css"; +import { useUserState } from "../state/useUserState"; +import { TodoComponent } from "../App"; - -const RenderField = ({ control, register, name, property, uiSchema, errors }) => { - if (property.type === 'object') { +const RenderField = ({ + control, + register, + name, + property, + uiSchema, + errors, +}) => { + if (property.type === "object") { return ( -
+

{property.title}

- {Object.keys(property.properties).map(subKey => ( + {Object.keys(property.properties).map((subKey) => ( ))}
); - } else if (property.type === 'array') { + } else if (property.type === "array") { const { fields, append, remove } = useFieldArray({ control, name: name, }); return ( -
+

{property.title}

{fields.map((field, index) => ( -
- {Object.keys(property.items.properties).map(subKey => ( +
+ {Object.keys(property.items.properties).map((subKey) => ( errors={errors} /> ))} - +
))} - +
); } else { return ( -
+
rules={{ required: property.required }} render={({ field }) => ( )} /> - {errors[name] &&
{property.title} is required
} + {errors[name] && ( +
{property.title} is required
+ )}
); } @@ -76,9 +95,15 @@ const RenderField = ({ control, register, name, property, uiSchema, errors }) => const TabForm = ({ schema, uiSchema, tabs, conditionalTabs }) => { const [currentTab, setCurrentTab] = useState(0); - const { control, register, handleSubmit, watch, formState: { errors } } = useForm(); + const { + control, + register, + handleSubmit, + watch, + formState: { errors }, + } = useForm(); - const onSubmit = data => console.log(data); + const onSubmit = (data) => console.log(data); const isTabVisible = (tab) => { if (!conditionalTabs[tab]) return true; @@ -88,219 +113,254 @@ const TabForm = ({ schema, uiSchema, tabs, conditionalTabs }) => { }; return ( -
- setCurrentTab(index)}> + + setCurrentTab(index)} + > - {tabs.map((tab, index) => ( - isTabVisible(index) && {tab.label} - ))} + {tabs.map( + (tab, index) => + isTabVisible(index) && {tab.label} + )} - {tabs.map((tab, index) => ( - isTabVisible(index) && ( - -

{tab.label}

- {tab.fields.map(key => ( - - ))} - {currentTab > 0 && } - {currentTab < tabs.length - 1 && } - {currentTab === tabs.length - 1 && } -
- ) - ))} + {tabs.map( + (tab, index) => + isTabVisible(index) && ( + +

{tab.label}

+ {tab.fields.map((key) => ( + + ))} + {currentTab > 0 && ( + + )} + {currentTab < tabs.length - 1 && ( + + )} + {currentTab === tabs.length - 1 && ( + + )} +
+ ) + )}
); }; - - const tabs = [ { label: "Personal Info", fields: ["personalInfo"] }, { label: "Address", fields: ["address"] }, { label: "Phones", fields: ["phones"] }, - { label: "Preferences", fields: ["preferences"] } + { label: "Preferences", fields: ["preferences"] }, ]; const conditionalTabs = { 3: { fields: ["preferences.newsletter"], - rule: values => values["preferences.newsletter"] - } + rule: (values) => values["preferences.newsletter"], + }, }; const TabFormScreen = () => { + const { setData, resetData, data } = useUserState(); + return (
- +

Hi {data?.name}

+ +
); -} +}; export default TabFormScreen; - - const schema = { - "title": "Complex Form", - "type": "object", - "properties": { - "personalInfo": { - "type": "object", - "title": "Personal Information", - "properties": { - "firstName": { - "type": "string", - "title": "First Name" - }, - "lastName": { - "type": "string", - "title": "Last Name" - }, - "age": { - "type": "integer", - "title": "Age" - }, - "email": { - "type": "string", - "format": "email", - "title": "Email" - } - } +const schema = { + title: "Complex Form", + type: "object", + properties: { + personalInfo: { + type: "object", + title: "Personal Information", + properties: { + firstName: { + type: "string", + title: "First Name", + }, + lastName: { + type: "string", + title: "Last Name", + }, + age: { + type: "integer", + title: "Age", + }, + email: { + type: "string", + format: "email", + title: "Email", + }, }, - "address": { - "type": "object", - "title": "Address", - "properties": { - "street": { - "type": "string", - "title": "Street" - }, - "city": { - "type": "string", - "title": "City" + }, + address: { + type: "object", + title: "Address", + properties: { + street: { + type: "string", + title: "Street", + }, + city: { + type: "string", + title: "City", + }, + state: { + type: "string", + title: "State", + }, + zipCode: { + type: "string", + title: "Zip Code", + }, + }, + }, + phones: { + type: "array", + title: "Phone Numbers", + items: { + type: "object", + properties: { + type: { + type: "string", + title: "Type", + enum: ["Home", "Work", "Mobile"], }, - "state": { - "type": "string", - "title": "State" + number: { + type: "string", + title: "Number", }, - "zipCode": { - "type": "string", - "title": "Zip Code" - } - } + }, }, - "phones": { - "type": "array", - "title": "Phone Numbers", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "title": "Type", - "enum": ["Home", "Work", "Mobile"] + }, + preferences: { + type: "object", + title: "Preferences", + properties: { + newsletter: { + type: "boolean", + title: "Receive Newsletter", + }, + notifications: { + type: "object", + title: "Notifications", + properties: { + email: { + type: "boolean", + title: "Email", + }, + sms: { + type: "boolean", + title: "SMS", + }, + push: { + type: "boolean", + title: "Push Notifications", }, - "number": { - "type": "string", - "title": "Number" - } - } - } - }, - "preferences": { - "type": "object", - "title": "Preferences", - "properties": { - "newsletter": { - "type": "boolean", - "title": "Receive Newsletter" }, - "notifications": { - "type": "object", - "title": "Notifications", - "properties": { - "email": { - "type": "boolean", - "title": "Email" - }, - "sms": { - "type": "boolean", - "title": "SMS" - }, - "push": { - "type": "boolean", - "title": "Push Notifications" - } - } - } - } - } - }, - "required": ["personalInfo", "email"] - }; - - const uiSchema = { - "ui:groups": { - "personalInfo": ["firstName", "lastName", "age", "email"], - "address": ["street", "city", "state", "zipCode"], - "phones": ["phones"], - "preferences": ["newsletter", "notifications"] - }, - "firstName": { - "ui:widget": "text" - }, - "lastName": { - "ui:widget": "text" - }, - "age": { - "ui:widget": "number" - }, - "email": { - "ui:widget": "email" - }, - "street": { - "ui:widget": "text" - }, - "city": { - "ui:widget": "text" + }, + }, }, - "state": { - "ui:widget": "text" + }, + required: ["personalInfo", "email"], +}; + +const uiSchema = { + "ui:groups": { + personalInfo: ["firstName", "lastName", "age", "email"], + address: ["street", "city", "state", "zipCode"], + phones: ["phones"], + preferences: ["newsletter", "notifications"], + }, + firstName: { + "ui:widget": "text", + }, + lastName: { + "ui:widget": "text", + }, + age: { + "ui:widget": "number", + }, + email: { + "ui:widget": "email", + }, + street: { + "ui:widget": "text", + }, + city: { + "ui:widget": "text", + }, + state: { + "ui:widget": "text", + }, + zipCode: { + "ui:widget": "text", + }, + phones: { + items: { + type: { + "ui:widget": "select", + }, + number: { + "ui:widget": "text", + }, }, - "zipCode": { - "ui:widget": "text" + }, + newsletter: { + "ui:widget": "checkbox", + }, + notifications: { + email: { + "ui:widget": "checkbox", }, - "phones": { - "items": { - "type": { - "ui:widget": "select" - }, - "number": { - "ui:widget": "text" - } - } + sms: { + "ui:widget": "checkbox", }, - "newsletter": { - "ui:widget": "checkbox" + push: { + "ui:widget": "checkbox", }, - "notifications": { - "email": { - "ui:widget": "checkbox" - }, - "sms": { - "ui:widget": "checkbox" - }, - "push": { - "ui:widget": "checkbox" - } - } - }; \ No newline at end of file + }, +}; diff --git a/micro-frontends/sample/src/pages/test.jsx b/micro-frontends/sample/src/pages/test.jsx index 8d6470cd76a..d7327a9f1fb 100644 --- a/micro-frontends/sample/src/pages/test.jsx +++ b/micro-frontends/sample/src/pages/test.jsx @@ -1,9 +1,10 @@ // src/components/JsonSchemaForm.js -import React, { useMemo, useCallback, use ,useEffect} from "react"; -import { useForm, Controller, useFieldArray ,useWatch} from "react-hook-form"; +import React, { useMemo, useCallback, use, useEffect } from "react"; +import { useForm, Controller, useFieldArray, useWatch } from "react-hook-form"; import CustomDatePicker from "./CustomCheck"; // Import the custom component import { ThemeContext } from "../examples/Theme"; import useLastUpdatedField from "../hooks/useLastUpdatedField"; +import { useUserState } from "../state/useUserState"; const schema = { title: "Complex Form", @@ -148,7 +149,7 @@ const RenderIndividualField = React.memo( ({ name, property, uiWidget, control, errors }) => { const CustomWidget = customWidgets[uiWidget]; const { theme, toggleTheme } = use(ThemeContext); - + return (