Skip to content

Commit

Permalink
fix: owner-contextのリファクタ
Browse files Browse the repository at this point in the history
  • Loading branch information
narirou committed Dec 4, 2024
1 parent 209af1b commit 7966a3c
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 144 deletions.
35 changes: 19 additions & 16 deletions frontend/app/components/primitives/form/date.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, PropsWithoutRef } from "react";
import { ComponentProps, FC, PropsWithoutRef } from "react";
import { twMerge } from "tailwind-merge";

type DateInputProps = PropsWithoutRef<{
Expand All @@ -8,31 +8,34 @@ type DateInputProps = PropsWithoutRef<{
defaultValue?: string;
className?: string;
required?: boolean;
size?: "sm" | "md";
onChange?: React.ChangeEventHandler<HTMLInputElement>;
}>;
}> &
ComponentProps<"input">;

export const DateInput: FC<DateInputProps> = (props) => {
export const DateInput: FC<DateInputProps> = ({
label,
id,
name,
className,
...props
}) => {
return (
<>
{props.label ? (
<label htmlFor={props.name} className="ps-1 text-gray-500">
{props.label}
{label ? (
<label htmlFor={id} className="ps-1 text-gray-500">
{label}
</label>
) : null}
<input
type="date"
id={props.id}
name={props.name}
defaultValue={props.defaultValue}
id={id}
name={name}
className={twMerge(
"mt-1 w-full border border-neutral-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",
(props.size ?? "md") === "md" ? "px-5 py-3" : "px-3 py-2",
props?.className,
"mt-1 w-full border border-neutral-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 px-3 py-2",
className,
)}
required={props.required}
onChange={props.onChange}
></input>
{...props}
/>
</>
);
};
136 changes: 66 additions & 70 deletions frontend/app/contexts/owner-context.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useNavigate, useSearchParams } from "@remix-run/react";
import { useNavigate } from "@remix-run/react";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
Expand All @@ -16,85 +16,85 @@ import {
import { isClientApiError } from "~/types";
import { getCookieValue } from "~/utils/get-cookie-value";

type DateString = `${number}-${number}-${number}`; // YYYY-MM-DD

type OwnerContextProps = Partial<{
chairs: OwnerGetChairsResponse["chairs"];
sales: OwnerGetSalesResponse;
provider: {
chairs?: OwnerGetChairsResponse["chairs"];
sales?: OwnerGetSalesResponse;
provider?: {
id: string;
name: string;
};
until?: DateString;
since?: DateString;
setUntil?: (date: string) => void;
setSince?: (date: string) => void;
}>;

// TODO
const DUMMY_DATA = {
total_sales: 8087,
chairs: [
{ id: "chair-a", name: "椅子A", sales: 999 },
{ id: "chair-b", name: "椅子B", sales: 999 },
],
models: [
{ model: "モデルA", sales: 999 },
{ model: "モデルB", sales: 999 },
],
} as const satisfies OwnerGetSalesResponse;
const OwnerContext = createContext<OwnerContextProps>({});

const OwnerContext = createContext<Partial<OwnerContextProps>>({});
const timestamp = (date: DateString) => {
return Math.floor(new Date(date).getTime() / 1000);
};

const timestamp = (date: string) => Math.floor(new Date(date).getTime());
const currentDateString: DateString = (() => {
const offset = new Date().getTimezoneOffset() * 60000;
const today = new Date(Date.now() - offset);
return today.toISOString().slice(0, 10) as DateString;
})();

export const OwnerProvider = ({ children }: { children: ReactNode }) => {
// TODO: 消す
const [searchParams] = useSearchParams();
const [chairs, setChairs] = useState<OwnerGetChairsResponse["chairs"]>();
const [sales, setSales] = useState<OwnerGetSalesResponse>();
const navigate = useNavigate();
const id = searchParams.get("id") ?? undefined;
const name = searchParams.get("name") ?? undefined;
const since = searchParams.get("since") ?? undefined;
const until = searchParams.get("until") ?? undefined;
const [until, _setUntil] = useState(currentDateString);
const [since, _setSince] = useState(currentDateString);

// TODO: 消す
const isDummy = useMemo(() => {
try {
const isDummy = sessionStorage.getItem("is-dummy-for-provider");
return isDummy === "true";
} catch (e) {
if (typeof e === "string") {
console.error(`CONSOLE ERROR: ${e}`);
}
return false;
const setUntil = useCallback((value: string) => {
if (/\d{4}-\d{2}-\d{2}/.test(value)) {
_setUntil(value as DateString);
}
}, []);

const [chairs, setChairs] = useState<OwnerGetChairsResponse>();
const [sales, setSales] = useState<OwnerGetSalesResponse>();
const setSince = useCallback((value: string) => {
if (/\d{4}-\d{2}-\d{2}/.test(value)) {
_setSince(value as DateString);
}
}, []);

useEffect(() => {
if (isDummy) {
setSales({
total_sales: DUMMY_DATA.total_sales,
chairs: DUMMY_DATA.chairs,
models: DUMMY_DATA.models,
});
} else {
Promise.all([
fetchOwnerGetChairs({}).then((res) => setChairs(res)),
since && until
? fetchOwnerGetSales({
queryParams: {
since: timestamp(since),
until: timestamp(until),
},
}).then((res) => setSales(res))
: Promise.resolve(),
]).catch((e) => {
console.error(e);
if (isClientApiError(e)) {
if (e.stack.status === 401) {
void (async () => {
try {
const data = await fetchOwnerGetChairs({});
setChairs(data.chairs);
} catch (error) {
console.error(error);
}
})();
}, []);

useEffect(() => {
void (async () => {
try {
const sales = await fetchOwnerGetSales({
// TODO: 機能していない?
queryParams: {
since: timestamp(since),
until: timestamp(until),
},
});
setSales(sales);
} catch (error) {
if (isClientApiError(error)) {
if (error.stack.status === 401) {
navigate("/owner/register");
return;
}
}
});
}
}, [setChairs, setSales, since, until, isDummy, navigate]);
console.error(error);
}
})();
}, [navigate, setChairs, setSales, since, until]);

useEffect(() => {
const isRegistered =
Expand All @@ -104,16 +104,12 @@ export const OwnerProvider = ({ children }: { children: ReactNode }) => {
}
}, [navigate]);

const props = useMemo<OwnerContextProps>(() => {
return {
chairs: chairs?.chairs ?? [],
sales,
provider: id && name ? { id, name } : undefined,
};
}, [chairs, sales, id, name]);

return (
<OwnerContext.Provider value={props}>{children}</OwnerContext.Provider>
<OwnerContext.Provider
value={{ chairs, sales, until, since, setUntil, setSince }}
>
{children}
</OwnerContext.Provider>
);
};

Expand Down
65 changes: 7 additions & 58 deletions frontend/app/routes/owner.sales/route.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { MetaFunction } from "@remix-run/node";
import { useEffect, useMemo, useState } from "react";
import {
OwnerGetSalesResponse as OwnerSalesType,
fetchOwnerGetSales,
} from "~/api/api-components";
import { useMemo, useState } from "react";
import { ChairIcon } from "~/components/icon/chair";
import { PriceText } from "~/components/modules/price-text/price-text";
import { Price } from "~/components/modules/price/price";
Expand All @@ -18,55 +14,16 @@ export const meta: MetaFunction = () => {
];
};

const timestamp = (date: string) => Math.floor(new Date(date).getTime() / 1000);

const viewTypes = [
{ key: "chair", label: "椅子別" },
{ key: "model", label: "モデル別" },
] as const;

const currentDateString = (() => {
const now = new Date();
return `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
})();

export default function Index() {
const [viewType, setViewType] =
useState<(typeof viewTypes)[number]["key"]>("chair");

const { chairs } = useOwnerContext();
const [salesDate, setSalesDate] = useState<{
since?: string;
until?: string;
}>({ since: currentDateString, until: currentDateString });
const [sales, setSales] = useState<OwnerSalesType>();

useEffect(() => {
const since = salesDate?.since;
const until = salesDate?.until;
if (!since || !until) return;
let abortController: AbortController | undefined;
void (async () => {
try {
abortController = new AbortController();
setSales(
await fetchOwnerGetSales(
{
queryParams: {
until: timestamp(until),
since: timestamp(since),
},
},
abortController.signal,
),
);
} catch (error) {
console.error(error);
}
})();

return () => abortController?.abort();
}, [salesDate, setSales]);
const { chairs, since, until, sales, setSince, setUntil } = useOwnerContext();

const chairModelMap = useMemo(
() => new Map(chairs?.map((c) => [c.id, c.model])),
Expand All @@ -92,32 +49,24 @@ export default function Index() {
}));
}, [sales, chairs, viewType, chairModelMap]);

const updateDate = (key: "since" | "until", value: string) => {
setSalesDate((prev) => {
return { ...prev, [key]: value };
});
};

return (
<div className="min-w-[800px] w-full">
<div className="flex items-center justify-between">
<div className="flex items-baseline gap-2">
<DateInput
id="sales-since"
name="since"
size="sm"
className="w-48 ms-[2px]"
defaultValue={salesDate.since}
onChange={(e) => updateDate("since", e.target.value)}
className="w-48"
defaultValue={since}
onChange={(e) => setSince?.(e.target.value)}
/>
<DateInput
id="sales-until"
name="until"
size="sm"
className="w-48"
defaultValue={salesDate.until}
onChange={(e) => updateDate("until", e.target.value)}
defaultValue={until}
onChange={(e) => setUntil?.(e.target.value)}
/>
</div>
{sales ? null : null}
Expand Down

0 comments on commit 7966a3c

Please sign in to comment.