diff --git a/src/components/landing-page/create-production.tsx b/src/components/landing-page/create-production.tsx
index 33cc0d30..f767efd1 100644
--- a/src/components/landing-page/create-production.tsx
+++ b/src/components/landing-page/create-production.tsx
@@ -13,7 +13,7 @@ import {
} from "./form-elements.tsx";
import { API } from "../../api/api.ts";
import { useGlobalState } from "../../global-state/context-provider.tsx";
-import { Loader } from "../loader/loader.tsx";
+import { Spinner } from "../loader/loader.tsx";
import { isMobile } from "../../bowser.ts";
type FormValues = {
@@ -154,7 +154,7 @@ export const CreateProduction = () => {
onClick={handleSubmit(onSubmit)}
>
Create Production
- {loading && }
+ {loading && }
{createdProductionId !== null && (
diff --git a/src/components/landing-page/productions-list.tsx b/src/components/landing-page/productions-list.tsx
index b7f4adb6..318076d4 100644
--- a/src/components/landing-page/productions-list.tsx
+++ b/src/components/landing-page/productions-list.tsx
@@ -3,6 +3,8 @@ import { useEffect, useState } from "react";
import { API } from "../../api/api.ts";
import { TProduction } from "../production-line/types.ts";
import { useGlobalState } from "../../global-state/context-provider.tsx";
+import { LoaderDots } from "../loader/loader.tsx";
+import { useRefreshAnimation } from "./use-refresh-animation.ts";
const ProductionListContainer = styled.div`
display: flex;
@@ -41,11 +43,9 @@ export const ProductionsList = () => {
let aborted = false;
if (reloadProductionList || intervalLoad) {
- setIntervalLoad(false);
API.listProductions()
.then((result) => {
if (aborted) return;
-
setProductions(
result
// pick laste 10 items
@@ -70,6 +70,7 @@ export const ProductionsList = () => {
dispatch({
type: "PRODUCTION_LIST_FETCHED",
});
+ setIntervalLoad(false);
})
.catch(() => {
// TODO handle error/retry
@@ -79,7 +80,9 @@ export const ProductionsList = () => {
return () => {
aborted = true;
};
- }, [dispatch, reloadProductionList, intervalLoad]);
+ }, [dispatch, intervalLoad, reloadProductionList]);
+
+ const showRefreshing = useRefreshAnimation({ reloadProductionList });
useEffect(() => {
const interval = window.setInterval(() => {
@@ -92,16 +95,16 @@ export const ProductionsList = () => {
}, []);
return (
-
- {/* // TODO handle so future load-component isn't shown on every update
- // TODO ex className={loading && !intervalLoad ? "active" : "in-active"} */}
- {/* TODO add loading indicator */}
- {productions.map((p) => (
-
- {p.name}
- {p.id}
-
- ))}
-
+ <>
+
+
+ {productions.map((p) => (
+
+ {p.name}
+ {p.id}
+
+ ))}
+
+ >
);
};
diff --git a/src/components/landing-page/use-refresh-animation.ts b/src/components/landing-page/use-refresh-animation.ts
new file mode 100644
index 00000000..0d61ee2c
--- /dev/null
+++ b/src/components/landing-page/use-refresh-animation.ts
@@ -0,0 +1,35 @@
+import { useEffect, useState } from "react";
+
+type TUseRefreshAnimationOptions = {
+ reloadProductionList: boolean;
+};
+
+export const useRefreshAnimation = ({
+ reloadProductionList,
+}: TUseRefreshAnimationOptions) => {
+ const [showRefreshing, setShowRefreshing] = useState(true);
+
+ useEffect(() => {
+ let timeout: number | null = null;
+
+ if (showRefreshing) {
+ timeout = window.setTimeout(() => {
+ setShowRefreshing(false);
+ }, 1500);
+ }
+
+ return () => {
+ if (timeout !== null) {
+ window.clearTimeout(timeout);
+ }
+ };
+ }, [showRefreshing]);
+
+ useEffect(() => {
+ if (reloadProductionList) {
+ setShowRefreshing(true);
+ }
+ }, [reloadProductionList]);
+
+ return showRefreshing;
+};
diff --git a/src/components/loader/loader.tsx b/src/components/loader/loader.tsx
index 14e4b8e9..6a3c235c 100644
--- a/src/components/loader/loader.tsx
+++ b/src/components/loader/loader.tsx
@@ -1,5 +1,5 @@
import styled from "@emotion/styled";
-import { FC } from "react";
+import { FC, useEffect, useState } from "react";
const Loading = styled.div`
border: 4px solid rgba(0, 0, 0, 0.1);
@@ -49,8 +49,52 @@ const Loading = styled.div`
}
`;
+const Text = styled.span`
+ padding-left: 2rem;
+ font-size: 1.8rem;
+ &.active {
+ color: #cdcdcd;
+ }
+ &.in-active {
+ color: #242424;
+ }
+`;
+
+const Dots = styled.span`
+ padding-left: 0.2rem;
+ font-size: 2rem;
+ transform: translateY(-50%);
+ &.active {
+ color: #cdcdcd;
+ }
+ &.in-active {
+ color: #242424;
+ }
+`;
+
type Props = { className: string };
-export const Loader: FC = ({ className }: Props) => {
+export const Spinner: FC = ({ className }: Props) => {
return ;
};
+
+export const LoaderDots: FC = ({ className }: Props) => {
+ const [dots, setDots] = useState(".");
+
+ useEffect(() => {
+ const intervalId = window.setInterval(() => {
+ setDots((prevDots) => (prevDots.length > 2 ? "." : `${prevDots}.`));
+ }, 300);
+
+ return () => {
+ clearInterval(intervalId);
+ };
+ }, []);
+
+ return (
+
+ refreshing
+ {dots}
+
+ );
+};
diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx
index b7c19123..dbb04072 100644
--- a/src/components/production-line/production-line.tsx
+++ b/src/components/production-line/production-line.tsx
@@ -9,7 +9,7 @@ import { ActionButton } from "../landing-page/form-elements.tsx";
import { UserList } from "./user-list.tsx";
import { API } from "../../api/api.ts";
import { noop } from "../../helpers.ts";
-import { Loader } from "../loader/loader.tsx";
+import { Spinner } from "../loader/loader.tsx";
const TempDiv = styled.div`
padding: 1rem;
@@ -91,7 +91,7 @@ export const ProductionLine: FC = () => {
Exit
- {loading && }
+ {loading && }
{!loading && (
<>
Production View