@tactics/luwio-app
v0.0.65
Published
Common components for all application powered by Luwio
Downloads
2,196
Readme
Luwio-Application
Common tools for all applications powered by Luwio
Internationalization
State: P.O.C
This is a wrapper around I18n (https://react.i18next.com/).
We do this to standardize everything around internationalization in all Luwio apps. This way we also prevent vendor detail from leaking into the entire code base of every app.
Initializing the internationalization.
This will return a Promise containing a Result Monad (Success or Failure). (@tactics/js-monad)
const MaybeI18n = Internationalization({
fetch: "https://api.luwio.be/api/shop/translations/{{lng}}",
app: "shop"
domain: "luwio.sport.vlaanderen"
language: "nl",
defaults: {},
headers: () => ({}),
showMissing: import.meta.env.MODE === "development",
}).bootstrap();
Passing to the provider.
You can then unwrap the Result when successful and pass it to the internationalizationProvider. Along with a default language and all supported languages of the app.
const I18n = MaybeI18n.unwrap();
<InternationalizationProvider
i18n={I18n}
defaultLanguage={"nl"}
supportedLanguages={["nl", "en", "fr"]}
>
{children}
</InternationalizationProvider>
Internationalization hooks.
Then we provide all hooks you needs for translations in your app.
const t = useTranslate();
const {changeLanguage, currentLanguage, defaultLanguage, supportedLanguages} = useLanguage();
Components
When bootstrapping a Luwio App, we first need to load the info about the App before we can render a page. Instead of showing a blank page during this setup, you can use the Luwio components to show a loading screen to the user.
if (MaybeI18n instanceof Awaiting) {
return <Bootstrap />;
}
When something fails during the bootstrap fase, we need to show a generic error page. You can use the bootstrap failure component to do so.
if (MaybeI18n instanceof Failure) {
return <BootstrapFailure />;
}
LuwioRouter
State: Beta
The LuwioRouter
is a powerful routing solution designed specifically for modern browser-based applications.
Built on top of the robust @tanstack/router,
it simplifies routing by exposing only the most essential, curated features developers need.
Read all about the LuwioRouter
Storage
State: P.O.C
Javascript has a default Storage interface and has 2 classes implementing this. Our storage interacts with this interface and adds a third storage, cookie storage.
window.sessionStorage : data in sessionStorage is cleared when the page session ends. (https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)
window.localStorage : localStorage data has no expiration time. (https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
const StorageProvider = window.sessionStorage;
const [maybeThemeMode, setRawThemeMode] = useStorage<ThemeMode>("theme_mode", ThemeMode.STANDARD, StorageProvider, serialize, deserialize);
const StorageProvider = window.localStorage;
const [maybeThemeMode, setRawThemeMode] = useStorage<ThemeMode>("theme_mode", ThemeMode.STANDARD, StorageProvider, serialize, deserialize);
- CookieStorage : store in Cookie on machine.
Due to the extra options a Cookie Storage has and that it needs to comply to the Storage interface. We add the extra Cookie specific options optionally in the contructor of the storage.
// With extra cookie options
const StorageProvider = new CookieStorage(
{
expires: new Date('2025-08-14T15:30:00'),
path: '/',
domain: '.luwio.be',
secure: true,
sameSite: "Lax"
}
);
...
Another benefit of our Storage implementation is that we require serialization and deserialization functions inside our hook. This allows you to have fine-grained control over how the item is put into storage and read out of it.
Serialization is straightforward most of the time and will pretty much be the same for all items. It is just converting the item you want to store to a string.
Having fine-grained control over deserialization is the main benefit here. Since the structure of an object that was put into storage can change over time as your project evolves. But the old structure can still be present on the users device, you don't have control over this.
In the deserialization you can include some validation and when the object can't be recreated you just pass a Failure along, as-if the item did not exist in storage and move along.
interface UserProfile {
name: string;
age: number;
}
const serializeUserProfile = (profile: UserProfile): Result<string> => {
try {
const jsonString = JSON.stringify(profile);
return Success.of(jsonString);
} catch (error) {
return Failure.dueTo("Failed to serialize user profile", "SERIALIZE_FAILURE");
}
};
const deserializeUserProfile = (raw: string): Result<UserProfile> => {
try {
const parsed = JSON.parse(raw);
if (typeof parsed.name === "string" && typeof parsed.age === "number") {
return Success.of(parsed as UserProfile);
} else {
return Failure.dueTo("Invalid user profile format", "INVALID_FORMAT");
}
} catch (error) {
return Failure.dueTo("Failed to deserialize user profile", "DESERIALIZE_FAILURE");
}
};
const [user, setUser, clearUser] = useStorage<UserProfile>(
'user',
null,
window.localStorage,
serializeUserProfile,
deserializeUserProfile
);
Error Handling
State: P.O.C
DateTime
State: P.O.C
Auth
State: TODO
Theming
State: TODO