Skip to content

Commit

Permalink
Merge pull request #27 from toririm/main-prepare-merge
Browse files Browse the repository at this point in the history
[main] sync up to date
  • Loading branch information
toririm authored Aug 26, 2024
2 parents da9b3b9 + 7b771e9 commit 12b77b2
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 19 deletions.
4 changes: 3 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
{}
{
"plugins": ["prettier-plugin-tailwindcss"]
}
15 changes: 9 additions & 6 deletions app/firebase/converter.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import type {
DocumentData,
QueryDocumentSnapshot,
SnapshotOptions,
import {
type DocumentData,
type QueryDocumentSnapshot,
type SnapshotOptions,
Timestamp,
} from "firebase/firestore";
import _ from "lodash";
import type { ZodSchema } from "zod";

export const converter = <T>(schema: ZodSchema<T>) => {
return {
toFirestore: (data: T) => {
return data as DocumentData;
// id は ドキュメントには含めない
const dataWithoutId = _.omit(data as object, "id");
return dataWithoutId as T;
},
fromFirestore: (
snapshot: QueryDocumentSnapshot,
Expand All @@ -31,7 +34,7 @@ export const converter = <T>(schema: ZodSchema<T>) => {
const parseDateProperty = (data: DocumentData): DocumentData => {
const parsedData = _.mapValues(data, (value) =>
// toDate が存在する場合は Timestamp 型としてパースする
value?.toDate ? value.toDate() : value,
value instanceof Timestamp ? value.toDate() : value,
);
const recursivelyParsedData = _.mapValues(parsedData, (value) => {
// 再帰的にパースする
Expand Down
3 changes: 3 additions & 0 deletions app/lib/typeguard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const hasId = <T>(obj: T): obj is Required<T> => {
return (obj as any).id !== undefined;
};
2 changes: 2 additions & 0 deletions app/models/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ export const itemSchema = z.object({

export type Item = z.infer<typeof itemSchema>;

export type ItemWithId = Required<Item>;

export type ItemType = Pick<Item, "type">["type"];
2 changes: 2 additions & 0 deletions app/models/order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export const orderSchema = z.object({
});

export type Order = z.infer<typeof orderSchema>;

export type OrderWithId = Required<Order>;
20 changes: 14 additions & 6 deletions app/repositories/item.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { Item } from "~/models/item";
import { Repository } from "./type";
import { addDoc, collection, getDocs } from "firebase/firestore";
import { converter } from "~/firebase/converter";
import { db } from "~/firebase/firestore";
import { Item, itemSchema } from "~/models/item";
import { ItemRepository } from "./type";

export const itemRepository: Repository<Item> = {
export const itemRepository: ItemRepository = {
findAll: async () => {
// ここに Firestore からデータを取得する処理を記述
return [];
const itemsRef = collection(db, "items").withConverter(
converter(itemSchema),
);
const docSnap = await getDocs(itemsRef);
const items = docSnap.docs.map((doc) => doc.data());
console.log(items);
return items;
},
findById: async (id: string) => {
// ここに Firestore からデータを取得する処理を記述
return null;
},
create: async (data: Item) => {
// ここに Firestore にデータを作成する処理を記述
const docRef = await addDoc(collection(db, "items"), data);
},
update: async (data: Item) => {
// ここに Firestore のデータを更新する処理を記述
Expand Down
67 changes: 67 additions & 0 deletions app/repositories/order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
addDoc,
collection,
deleteDoc,
doc,
getDoc,
getDocs,
setDoc,
} from "firebase/firestore";
import { converter } from "~/firebase/converter";
import { db } from "~/firebase/firestore";
import { hasId } from "~/lib/typeguard";
import { Order, orderSchema, OrderWithId } from "~/models/order";
import { OrderRepository } from "./type";

export const orderRepository: OrderRepository = {
save: async (order) => {
if (hasId(order)) {
return await update(order);
} else {
return await create(order);
}
},

delete: async (id) => {
await deleteDoc(doc(db, "orders", id));
},

findById: async (id) => {
const docRef = doc(db, "orders", id).withConverter(
converter(orderSchema.required()),
);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
return docSnap.data();
}
return null;
},

findAll: async () => {
const colRef = collection(db, "orders").withConverter(
converter(orderSchema.required()),
);
const docSnaps = await getDocs(colRef);
return docSnaps.docs.map((doc) => doc.data());
},
};

const update = async (order: OrderWithId): Promise<OrderWithId> => {
const docRef = doc(db, "orders", order.id).withConverter(
converter(orderSchema.required()),
);
await setDoc(docRef, order);
return order;
};

const create = async (order: Order): Promise<OrderWithId> => {
const colRef = collection(db, "orders").withConverter(converter(orderSchema));
const docRef = await addDoc(colRef, order);
const resultDoc = await getDoc(
docRef.withConverter(converter(orderSchema.required())),
);
if (resultDoc.exists()) {
return resultDoc.data();
}
throw new Error("Failed to save order");
};
18 changes: 12 additions & 6 deletions app/repositories/type.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
export type Repository<T> = {
findAll: () => Promise<T[]>;
findById: (id: string) => Promise<T | null>;
create: (data: T) => Promise<void>;
update: (data: T) => Promise<void>;
delete: (id: string) => Promise<void>;
import { Item } from "~/models/item";
import { Order } from "~/models/order";

export type BaseRepository<T> = {
save(data: T): Promise<Required<T>>;
delete(id: string): Promise<void>;
findById(id: string): Promise<Required<T> | null>;
findAll(): Promise<Required<T>[]>;
};

export type ItemRepository = BaseRepository<Item>;

export type OrderRepository = BaseRepository<Order>;
116 changes: 116 additions & 0 deletions app/routes/orders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import type { ActionFunction, MetaFunction } from "@remix-run/node";
import { Form } from "@remix-run/react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import { Order } from "~/models/order";
import { orderRepository } from "~/repositories/order";

export const meta: MetaFunction = () => {
return [{ title: "オーダー" }];
};

const type2label = {
hot: "ホット",
ice: "アイス",
ore: "オレ",
milk: "ミルク",
};

export const clientLoader = async () => {
console.log("findAllのテスト");
const orders = await orderRepository.findAll();
return typedjson({ orders });
};

export default function Orders() {
const { orders } = useTypedLoaderData<typeof clientLoader>();

return (
<div className="font-sans p-4">
<h1 className="text-3xl">アイテム</h1>
<ul>
{orders.map((order) => (
<li key={order.id}>
<h2>{order.orderId}</h2>
<p>{order.id}</p>
<div>
{order.items.map((item) => (
<div key={item.id}>
<h3>{item.name}</h3>
<p>{item.price}</p>
<p>{type2label[item.type]}</p>
</div>
))}
</div>
<p>{order.createdAt.toISOString()}</p>
<p>{`提供時間:${order.servedAt?.toISOString()}`}</p>
<p>{order.total}</p>
<p>{order.orderReady}</p>
</li>
))}
</ul>
<Form method="post">
<Input type="number" name="orderId" required placeholder="注文番号" />
<Button type="submit">登録</Button>
</Form>
<Form method="delete">
<Input type="text" name="id" required placeholder="id" />
<Button type="submit">削除</Button>
</Form>
<Form method="put">
<Input type="text" name="id" required placeholder="id" />
<Button type="submit">更新</Button>
</Form>
</div>
);
}

const testOrder = (orderId: number): Order => ({
orderId,
items: [
{
id: "1",
name: "テスト",
price: 100,
type: "hot",
},
],
createdAt: new Date(),
servedAt: null,
assignee: null,
total: 100,
orderReady: false,
});

export const clientAction: ActionFunction = async ({ request }) => {
const formData = await request.formData();

switch (request.method) {
case "POST":
console.log("save(create)のテスト");
const orderId = Number(formData.get("orderId"));
const newOrder = testOrder(orderId);
const savedOrder = await orderRepository.save(newOrder);
console.log("created", savedOrder);
break;

case "DELETE":
console.log("deleteのテスト");
const id = formData.get("id");
await orderRepository.delete(id as string);
break;

case "PUT":
console.log("save(update)のテスト");
const id2 = formData.get("id");
const order = await orderRepository.findById(id2 as string);
if (order) {
order.servedAt = new Date();
await orderRepository.save(order);
}
break;
}

return null;
};
Binary file modified bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"lucide-react": "^0.424.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"remix-typedjson": "^0.4.1",
"swr": "^2.2.5",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
Expand All @@ -48,6 +49,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.38",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"tailwindcss": "^3.4.4",
"typescript": "^5.1.6",
"vite": "^5.1.0",
Expand Down

0 comments on commit 12b77b2

Please sign in to comment.