Skip to content

Commit

Permalink
[スキーマ変更] レジの客用画面&オーダーストップ情報共有 (#354)
Browse files Browse the repository at this point in the history
closes #205
closes #217
  • Loading branch information
toririm authored Oct 30, 2024
1 parent a44ab7c commit 9496994
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 13 deletions.
37 changes: 37 additions & 0 deletions common/firebase-utils/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import {
import _ from "lodash";
import type { ZodSchema } from "zod";
import type { WithId } from "../lib/typeguard";
import {
type GlobalCashierState,
MasterStateEntity,
globalCashierStateSchema,
globalMasterStateSchema,
} from "../models/global";
import { ItemEntity, itemSchema } from "../models/item";
import { OrderEntity, orderSchema } from "../models/order";

Expand Down Expand Up @@ -95,3 +101,34 @@ export const orderConverter: FirestoreDataConverter<WithId<OrderEntity>> = {
return OrderEntity.fromOrder(convertedData);
},
};

export const cashierStateConverter: FirestoreDataConverter<GlobalCashierState> =
{
toFirestore: converter(globalCashierStateSchema).toFirestore,
fromFirestore: (
snapshot: QueryDocumentSnapshot,
options: SnapshotOptions,
) => {
const convertedData = converter(globalCashierStateSchema).fromFirestore(
snapshot,
options,
);

return convertedData;
},
};

export const masterStateConverter: FirestoreDataConverter<MasterStateEntity> = {
toFirestore: converter(globalMasterStateSchema).toFirestore,
fromFirestore: (
snapshot: QueryDocumentSnapshot,
options: SnapshotOptions,
) => {
const convertedData = converter(globalMasterStateSchema).fromFirestore(
snapshot,
options,
);

return MasterStateEntity.fromMasterState(convertedData);
},
};
10 changes: 7 additions & 3 deletions common/firebase-utils/firestore.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { type FirebaseOptions, initializeApp } from "firebase/app";
import { getFirestore, initializeFirestore } from "firebase/firestore";

const firebaseConfig = {
const firebaseConfig: FirebaseOptions = {
apiKey: "AIzaSyC3llKAZQOVQEFV0-0xHiseDB55YXJilHM",
authDomain: "cafeore-2024.firebaseapp.com",
projectId: "cafeore-2024",
Expand All @@ -12,4 +12,8 @@ const firebaseConfig = {

const app = initializeApp(firebaseConfig);

initializeFirestore(app, {
ignoreUndefinedProperties: true,
});

export const prodDB = getFirestore(app);
23 changes: 23 additions & 0 deletions common/firebase-utils/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
type FirestoreDataConverter,
type QueryConstraint,
collection,
doc,
onSnapshot,
query,
} from "firebase/firestore";
Expand Down Expand Up @@ -34,3 +35,25 @@ export const collectionSub = <T>(
};
return sub;
};

export const documentSub = <T>({
converter,
}: { converter: FirestoreDataConverter<T> }) => {
const sub: SWRSubscription<string[], T, Error> = (
[collectionName, ...keys],
{ next },
) => {
const coll = collection(prodDB, collectionName);
const unsub = onSnapshot(
doc(coll, ...keys).withConverter(converter),
(snapshot) => {
next(null, snapshot.data());
},
(err) => {
next(err);
},
);
return unsub;
};
return sub;
};
63 changes: 63 additions & 0 deletions common/models/global.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { z } from "zod";
import { orderSchema } from "./order";

export const globalCashierStateSchema = z.object({
id: z.literal("cashier-state"),
edittingOrder: orderSchema,
});

export type GlobalCashierState = z.infer<typeof globalCashierStateSchema>;

export const orderStatTypes = ["stop", "operational"] as const;

export const orderStatSchema = z.object({
createdAt: z.date(),
type: z.enum(orderStatTypes),
});

export type OrderStatType = (typeof orderStatTypes)[number];
export type OrderStat = z.infer<typeof orderStatSchema>;

export const globalMasterStateSchema = z.object({
id: z.literal("master-state"),
orderStats: z.array(orderStatSchema),
});

export type GlobalMasterState = z.infer<typeof globalMasterStateSchema>;

export const globalStatSchema = z.union([
globalCashierStateSchema,
globalMasterStateSchema,
]);

export type GlobalStat = z.infer<typeof globalStatSchema>;

export class MasterStateEntity implements GlobalMasterState {
constructor(
public id: "master-state",
private _orderStats: OrderStat[],
) {}

static fromMasterState(state: GlobalMasterState): MasterStateEntity {
return new MasterStateEntity(state.id, state.orderStats);
}

static createNew(): MasterStateEntity {
const initOrderStat: OrderStat = {
createdAt: new Date(),
type: "operational",
};
return new MasterStateEntity("master-state", [initOrderStat]);
}

get orderStats() {
return this._orderStats;
}

addOrderStat(stat: OrderStatType) {
this._orderStats.push({
createdAt: new Date(),
type: stat,
});
}
}
62 changes: 62 additions & 0 deletions common/repositories/global.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { type Firestore, doc, getDoc, setDoc } from "firebase/firestore";
import {
cashierStateConverter,
masterStateConverter,
} from "../firebase-utils/converter";
import { prodDB } from "../firebase-utils/firestore";
import type { GlobalCashierState, MasterStateEntity } from "../models/global";

export type CashierStateRepo = {
get: () => Promise<GlobalCashierState | undefined>;
set: (state: GlobalCashierState) => Promise<void>;
};

export type MasterStateRepo = {
get: () => Promise<MasterStateEntity | undefined>;
set: (state: MasterStateEntity) => Promise<void>;
};

export const cashierStateRepoFactory = (db: Firestore): CashierStateRepo => {
return {
get: async () => {
const docRef = doc(db, "global", "cashier-state").withConverter(
cashierStateConverter,
);
const docSnap = await getDoc(docRef);
const data = docSnap.data();
if (data?.id === "cashier-state") {
return data;
}
},
set: async (state) => {
const docRef = doc(db, "global", "cashier-state").withConverter(
cashierStateConverter,
);
await setDoc(docRef, state);
},
};
};

export const masterStateRepoFactory = (db: Firestore): MasterStateRepo => {
return {
get: async () => {
const docRef = doc(db, "global", "master-state").withConverter(
masterStateConverter,
);
const docSnap = await getDoc(docRef);
const data = docSnap.data();
if (data?.id === "master-state") {
return data;
}
},
set: async (state) => {
const docRef = doc(db, "global", "master-state").withConverter(
masterStateConverter,
);
await setDoc(docRef, state);
},
};
};

export const cashierRepository = cashierStateRepoFactory(prodDB);
export const masterRepository = masterStateRepoFactory(prodDB);
5 changes: 4 additions & 1 deletion firestore.rules
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ service cloud.firestore {
match /orders/{orderId} {
allow read, write: if true;
}
match /global/{docId} {
allow read, write: if true;
}
}
}
}
11 changes: 11 additions & 0 deletions pos/app/components/functional/useSyncCahiserOrder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { OrderEntity } from "common/models/order";
import { useEffect } from "react";

export const useSyncCahiserOrder = (
order: OrderEntity,
syncOrder: (order: OrderEntity) => void,
) => {
useEffect(() => {
syncOrder(order);
}, [order, syncOrder]);
};
5 changes: 4 additions & 1 deletion pos/app/components/pages/CashierV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useInputStatus } from "../functional/useInputStatus";
import { useLatestOrderId } from "../functional/useLatestOrderId";
import { useOrderState } from "../functional/useOrderState";
import { usePreventNumberKeyUpDown } from "../functional/usePreventNumberKeyUpDown";
import { useSyncCahiserOrder } from "../functional/useSyncCahiserOrder";
import { useUISession } from "../functional/useUISession";
import { AttractiveTextArea } from "../molecules/AttractiveTextArea";
import { InputHeader } from "../molecules/InputHeader";
Expand All @@ -24,14 +25,15 @@ type props = {
items: WithId<ItemEntity>[] | undefined;
orders: WithId<OrderEntity>[] | undefined;
submitPayload: (order: OrderEntity) => void;
syncOrder: (order: OrderEntity) => void;
};

/**
* キャッシャー画面のコンポーネント
*
* データの入出力は親コンポーネントに任せる
*/
const CashierV2 = ({ items, orders, submitPayload }: props) => {
const CashierV2 = ({ items, orders, submitPayload, syncOrder }: props) => {
const [newOrder, newOrderDispatch] = useOrderState();
const {
inputStatus,
Expand All @@ -44,6 +46,7 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => {
const [menuOpen, setMenuOpen] = useState(false);
const [UISession, renewUISession] = useUISession();
const { nextOrderId } = useLatestOrderId(orders);
useSyncCahiserOrder(newOrder, syncOrder);

const printer = usePrinter();

Expand Down
Loading

0 comments on commit 9496994

Please sign in to comment.