1// File: apps/koyo/lib/icecreamOrder/icecreamOrder.document.ts
2import { beyond, by, Database, into, type SchemaOf } from "@akanjs/document";
3
4import * as cnst from "../cnst";
5import { Revert } from "../dict";
6
7@Database.Input(() => cnst.IcecreamOrderInput)
8export class IcecreamOrderInput extends by(cnst.IcecreamOrderInput) {}
9
10@Database.Document(() => cnst.IcecreamOrder)
11export class IcecreamOrder extends by(cnst.IcecreamOrder) {
12 // Business method to start processing an order
13 process() {
14 if (this.status !== "active") {
15 throw new Revert("icecreamOrder.onlyActiveCanBeProcessed");
16 }
17 this.status = "processing";
18 return this;
19 }
20
21 // Business method to mark an order as served
22 serve() {
23 if (this.status !== "processing") {
24 throw new Revert("icecreamOrder.onlyProcessingCanBeServed");
25 }
26 this.status = "served";
27 return this;
28 }
29
30 // Business method to cancel an order
31 cancel() {
32 if (this.status !== "active") {
33 throw new Revert("icecreamOrder.onlyActiveCanBeCanceled");
34 }
35 this.status = "canceled";
36 return this;
37 }
38}
39// ...other code
1// File: apps/koyo/lib/icecreamOrder/icecreamOrder.dictionary.ts
2// ...other code
3const modelDictionary = {
4 // ...other code
5
6 // Error messages for business rule violations
7 onlyActiveCanBeProcessed: [
8 "Only active orders can be processed",
9 "활성 상태의 주문만 처리할 수 있습니다"
10 ],
11 onlyProcessingCanBeServed: [
12 "Only processing orders can be served",
13 "처리중인 주문만 서빙할 수 있습니다"
14 ],
15 onlyActiveCanBeCanceled: [
16 "Only active orders can be canceled",
17 "활성 상태의 주문만 취소할 수 있습니다"
18 ],
19 // * ==================== Etc ==================== * //
20} satisfies ModelDictionary<IcecreamOrder, IcecreamOrderInsight, IcecreamOrderFilter>;
21// ...other code
1// File: apps/koyo/lib/icecreamOrder/icecreamOrder.service.ts
2import { DbService, Service } from "@akanjs/service";
3
4import * as cnst from "../cnst";
5import * as db from "../db";
6
7@Service("IcecreamOrderService")
8export class IcecreamOrderService extends DbService(db.icecreamOrderDb) {
9
10 // Service method to process an ice cream order
11 async processIcecreamOrder(icecreamOrderId: string) {
12 const icecreamOrder = await this.getIcecreamOrder(icecreamOrderId);
13 return await icecreamOrder.process().save();
14 }
15
16 // Service method to serve an ice cream order
17 async serveIcecreamOrder(icecreamOrderId: string) {
18 const icecreamOrder = await this.getIcecreamOrder(icecreamOrderId);
19 return await icecreamOrder.serve().save();
20 }
21
22 // Service method to cancel an ice cream order
23 async cancelIcecreamOrder(icecreamOrderId: string) {
24 const icecreamOrder = await this.getIcecreamOrder(icecreamOrderId);
25 return await icecreamOrder.cancel().save();
26 }
27}
1// File: apps/koyo/lib/icecreamOrder/icecreamOrder.signal.ts
2import { ID, Int } from "@akanjs/base";
3import { SortOf } from "@akanjs/constant";
4import { Arg, DbSignal, Mutation, Query, resolve, Signal } from "@akanjs/signal";
5
6import * as cnst from "../cnst";
7
8@Signal(() => cnst.IcecreamOrder)
9export class IcecreamOrderSignal extends DbSignal(cnst.icecreamOrderCnst, cnst.Srvs, {
10 guards: { get: Query.Public, cru: Mutation.Public },
11}) {
12 // ...existing code for CRUD operations...
13
14 // Signal endpoint to process an ice cream order
15 @Mutation.Public(() => cnst.IcecreamOrder)
16 async processIcecreamOrder(@Arg.Param("icecreamOrderId", () => ID) icecreamOrderId: string) {
17 const icecreamOrder = await this.icecreamOrderService.processIcecreamOrder(icecreamOrderId);
18 return resolve<cnst.IcecreamOrder>(icecreamOrder);
19 }
20
21 // Signal endpoint to serve an ice cream order
22 @Mutation.Public(() => cnst.IcecreamOrder)
23 async serveIcecreamOrder(@Arg.Param("icecreamOrderId", () => ID) icecreamOrderId: string) {
24 const icecreamOrder = await this.icecreamOrderService.serveIcecreamOrder(icecreamOrderId);
25 return resolve<cnst.IcecreamOrder>(icecreamOrder);
26 }
27
28 // Signal endpoint to cancel an ice cream order
29 @Mutation.Public(() => cnst.IcecreamOrder)
30 async cancelIcecreamOrder(@Arg.Param("icecreamOrderId", () => ID) icecreamOrderId: string) {
31 const icecreamOrder = await this.icecreamOrderService.cancelIcecreamOrder(icecreamOrderId);
32 return resolve<cnst.IcecreamOrder>(icecreamOrder);
33 }
34}
1// File: apps/koyo/lib/icecreamOrder/icecreamOrder.dictionary.ts
2// ...other code
3const signalDictionary = {
4 ...getBaseSignalTrans("icecreamOrder" as const),
5 // * ==================== Endpoint ==================== * //
6 // ...existing endpoints...
7
8 // API endpoint translations for status operations
9 "api-processIcecreamOrder": ["Process", "작업시작"],
10 "apidesc-processIcecreamOrder": ["Start processing an ice cream order", "아이스크림 주문 처리를 시작합니다"],
11 "arg-processIcecreamOrder-icecreamOrderId": ["Order ID", "주문 ID"],
12 "argdesc-processIcecreamOrder-icecreamOrderId": ["ID of the order to process", "처리할 주문의 ID"],
13
14 "api-serveIcecreamOrder": ["Serve", "서빙완료"],
15 "apidesc-serveIcecreamOrder": ["Mark an ice cream order as served", "아이스크림 주문을 서빙완료로 표시합니다"],
16 "arg-serveIcecreamOrder-icecreamOrderId": ["Order ID", "주문 ID"],
17 "argdesc-serveIcecreamOrder-icecreamOrderId": ["ID of the order to serve", "서빙할 주문의 ID"],
18
19 "api-cancelIcecreamOrder": ["Cancel", "주문취소"],
20 "apidesc-cancelIcecreamOrder": ["Cancel an ice cream order", "아이스크림 주문을 취소합니다"],
21 "arg-cancelIcecreamOrder-icecreamOrderId": ["Order ID", "주문 ID"],
22 "argdesc-cancelIcecreamOrder-icecreamOrderId": ["ID of the order to cancel", "취소할 주문의 ID"],
23 // * ==================== Endpoint ==================== * //
24} satisfies SignalDictionary<IcecreamOrderSignal, IcecreamOrder>;
1// File: apps/koyo/lib/icecreamOrder/icecreamOrder.store.ts
2import { stateOf, Store } from "@akanjs/store";
3
4import * as cnst from "../cnst";
5import { fetch } from "../sig";
6
7@Store(() => cnst.IcecreamOrder)
8export class IcecreamOrderStore extends stateOf(fetch.icecreamOrderGql, {
9 // state
10}) {
11
12 // Store action to process an ice cream order
13 async processIcecreamOrder(icecreamOrderId: string) {
14 const icecreamOrder = await fetch.processIcecreamOrder(icecreamOrderId);
15 this.setIcecreamOrder(icecreamOrder);
16 }
17
18 // Store action to serve an ice cream order
19 async serveIcecreamOrder(icecreamOrderId: string) {
20 const icecreamOrder = await fetch.serveIcecreamOrder(icecreamOrderId);
21 this.setIcecreamOrder(icecreamOrder);
22 }
23
24 // Store action to cancel an ice cream order
25 async cancelIcecreamOrder(icecreamOrderId: string) {
26 const icecreamOrder = await fetch.cancelIcecreamOrder(icecreamOrderId);
27 this.setIcecreamOrder(icecreamOrder);
28 }
29}
1// File: apps/koyo/lib/icecreamOrder/IcecreamOrder.Util.tsx
2"use client";
3import { clsx } from "@akanjs/client";
4import { cnst, IcecreamOrder, st, usePage } from "@koyo/client";
5import { Model } from "@akanjs/ui";
6
7// ...existing ViewWrapper component...
8
9// Process button component
10interface ProcessProps {
11 className?: string;
12 icecreamOrderId: string;
13 disabled?: boolean;
14}
15export const Process = ({ className, icecreamOrderId, disabled }: ProcessProps) => {
16 const { l } = usePage();
17 return (
18 <button
19 className={clsx("btn btn-secondary", className)}
20 disabled={disabled}
21 onClick={() => {
22 void st.do.processIcecreamOrder(icecreamOrderId);
23 }}
24 >
25 {l("icecreamOrder.api-processIcecreamOrder")}
26 </button>
27 );
28};
29
30// Serve button component
31interface ServeProps {
32 className?: string;
33 icecreamOrderId: string;
34 disabled?: boolean;
35}
36export const Serve = ({ className, icecreamOrderId, disabled }: ServeProps) => {
37 const { l } = usePage();
38 return (
39 <button
40 className={clsx("btn btn-accent", className)}
41 disabled={disabled}
42 onClick={() => {
43 void st.do.serveIcecreamOrder(icecreamOrderId);
44 }}
45 >
46 {l("icecreamOrder.api-serveIcecreamOrder")}
47 </button>
48 );
49};
50
51// Cancel button component
52interface CancelProps {
53 className?: string;
54 icecreamOrderId: string;
55 disabled?: boolean;
56}
57export const Cancel = ({ className, icecreamOrderId, disabled }: CancelProps) => {
58 const { l } = usePage();
59 return (
60 <button
61 className={clsx("btn btn-warning", className)}
62 disabled={disabled}
63 onClick={() => {
64 void st.do.cancelIcecreamOrder(icecreamOrderId);
65 }}
66 >
67 {l("icecreamOrder.api-cancelIcecreamOrder")}
68 </button>
69 );
70};
1// File: apps/koyo/lib/icecreamOrder/IcecreamOrder.Unit.tsx
2import { clsx, ModelProps } from "@akanjs/client";
3import { cnst, IcecreamOrder, usePage } from "@koyo/client";
4
5export const Card = ({ icecreamOrder }: ModelProps<"icecreamOrder", cnst.LightIcecreamOrder>) => {
6 const { l } = usePage();
7 return (
8 <div className="group flex h-auto w-full flex-col overflow-hidden rounded-xl bg-gradient-to-br from-pink-100 via-yellow-50 to-pink-200 p-6 shadow-md transition-all duration-300 hover:shadow-xl">
9 {/* Order information section */}
10 <div className="flex w-full flex-col justify-center space-y-4">
11 <div className="flex items-center gap-2 text-lg font-semibold text-pink-700">
12 <span className="inline-block rounded bg-pink-200 px-2 py-1 text-xs font-bold tracking-wider uppercase">
13 {l.field("icecreamOrder", "id")}
14 </span>
15 <span className="ml-2 font-mono text-pink-900">{icecreamOrder.id}</span>
16 </div>
17 <div className="flex items-center gap-2">
18 <span className="inline-block rounded bg-yellow-200 px-2 py-1 text-xs font-bold tracking-wider text-yellow-800 uppercase">
19 {l.field("icecreamOrder", "status")}
20 </span>
21 <span
22 className={clsx("ml-2 rounded-full px-3 py-1 text-sm font-semibold", {
23 "bg-green-100 text-green-700": icecreamOrder.status === "active",
24 "bg-blue-100 text-blue-700": icecreamOrder.status === "processing",
25 "bg-red-100 text-red-700": icecreamOrder.status === "served",
26 "bg-gray-100 text-gray-700": icecreamOrder.status === "canceled",
27 })}
28 >
29 {l.enum("icecreamOrder", "status", icecreamOrder.status)}
30 </span>
31 </div>
32 </div>
33
34 {/* Action buttons section */}
35 <div className="bg-base-100/50 flex items-center justify-center gap-2 rounded-xl p-4">
36 <Model.ViewWrapper sliceName="icecreamOrder" modelId={icecreamOrder.id}>
37 <button className="btn btn-primary btn-xl">
38 <span>{l.trans({ en: "View", ko: "보기" })}</span>
39 </button>
40 </Model.ViewWrapper>
41 <IcecreamOrder.Util.Process icecreamOrderId={icecreamOrder.id} disabled={icecreamOrder.status !== "active"} />
42 <IcecreamOrder.Util.Serve icecreamOrderId={icecreamOrder.id} disabled={icecreamOrder.status !== "processing"} />
43 <IcecreamOrder.Util.Cancel icecreamOrderId={icecreamOrder.id} disabled={icecreamOrder.status !== "active"} />
44 </div>
45 </div>
46 );
47};
1// File: apps/koyo/lib/icecreamOrder/IcecreamOrder.View.tsx
2import { clsx } from "@akanjs/client";
3import { cnst, IcecreamOrder, usePage } from "@koyo/client";
4
5interface IcecreamOrderViewProps {
6 className?: string;
7 icecreamOrder: cnst.IcecreamOrder;
8}
9
10export const General = ({ className, icecreamOrder }: IcecreamOrderViewProps) => {
11 const { l } = usePage();
12 return (
13 <div className={clsx(className, "mx-auto w-full space-y-6 rounded-xl p-8 shadow-lg")}>
14 {/* Header section */}
15 <div className="flex items-center gap-3 border-b pb-4">
16 <span className="text-3xl font-extrabold text-pink-600">🍦</span>
17 <span className="text-2xl font-bold">{l("icecreamOrder.modelName")}</span>
18 <span className="ml-auto text-xs text-base-content/50">#{icecreamOrder.id}</span>
19 </div>
20
21 {/* Order details grid - same as before */}
22 <div className="grid grid-cols-2 gap-x-6 gap-y-4">
23 {/* ... existing field displays ... */}
24 </div>
25
26 {/* Action buttons section */}
27 <div className="flex items-center justify-end gap-2">
28 <IcecreamOrder.Util.Process icecreamOrderId={icecreamOrder.id} disabled={icecreamOrder.status !== "active"} />
29 <IcecreamOrder.Util.Serve icecreamOrderId={icecreamOrder.id} disabled={icecreamOrder.status !== "processing"} />
30 <IcecreamOrder.Util.Cancel icecreamOrderId={icecreamOrder.id} disabled={icecreamOrder.status !== "active"} />
31 </div>
32 </div>
33 );
34};