akan create-application koyo
akan start koyo
1 | Fruit Ring |
---|---|
2 | Oreo |
3 | Strawberry |
4 | Mango |
5 | Cheese Cube |
6 | Corn |
7 | Granola |
8 | Banana |
9 | Fig |
akan create-module icecreamOrder
# then select koyo application
└── apps/ # Application code
└── koyo/ # Individual application
└── lib/ # Domain modules
└── icecreamOrder/ # Icecream order domain module
├── icecreamOrder.constant.ts # Types and schemas
├── icecreamOrder.dictionary.ts # Translations
├── icecreamOrder.document.ts # Document
├── icecreamOrder.service.ts # Business logic
├── icecreamOrder.signal.ts # API endpoints
├── icecreamOrder.store.ts # State management
├── icecreamOrder.Template.tsx # Form UI
├── icecreamOrder.Unit.tsx # Overview UI
├── icecreamOrder.Util.tsx # Utility UI
├── icecreamOrder.View.tsx # Detail view UI
└── icecreamOrder.Zone.tsx # Integration UI
1
2// File: apps/koyo/lib/icecreamOrder/icecreamOrder.constant.ts
3import { enumOf, Int } from "@akanjs/base";
4import { Field, Filter, Model, sortOf, via } from "@akanjs/constant";
5
6export const IcecreamOrderStatus = enumOf(["active", "processing", "served", "canceled"] as const);
7export type IcecreamOrderStatus = enumOf<typeof IcecreamOrderStatus>;
8
9export const Topping = enumOf(["fruitRings", "oreo", "strawberry", "mango", "cheeseCube", "corn", "granola", "banana", "fig"] as const);
10export type Topping = enumOf<typeof Topping>;
11
12@Model.Input("IcecreamOrderInput")
13export class IcecreamOrderInput {
14 @Field.Prop(() => Int, { default: 50 })
15 size: 50 | 100 | 200;
16
17 @Field.Prop(() => [String], { enum: Topping })
18 toppings: Topping[];
19}
20// ...other code
1
2// File: apps/koyo/lib/icecreamOrder/icecreamOrder.dictionary.ts
3// ...other code
4const modelDictionary = {
5 ...baseTrans,
6 modelName: ["IcecreamOrder", "아이스크림 주문"],
7 modelDesc: [
8 "IcecreamOrder is an option that customers can choose when ordering icecream at koyo store.",
9 "아이스크림 주문은 koyo 가게에서 고객이 아이스크림을 주문할 때 커스텀할 수 있는 옵션들을 선택할 수 있습니다.",
10 ],
11
12 // * ==================== Model ==================== * //
13 size: ["Size", "사이즈"],
14 "desc-size": ["Size", "사이즈"],
15
16 toppings: ["Toppings", "토핑"],
17 "desc-toppings": ["Toppings", "토핑"],
18 // * ==================== Model ==================== * //
19
20 // ...other code
21
22 // * ==================== Etc ==================== * //
23 "enum-status-active": ["Active", "활성"],
24 "enumdesc-status-active": ["Active status", "활성 상태"],
25 "enum-status-processing": ["Processing", "처리중"],
26 "enumdesc-status-processing": ["Processing status", "처리중 상태"],
27 "enum-status-served": ["Served", "서빙완료"],
28 "enumdesc-status-served": ["Served status", "서빙완료 상태"],
29 "enum-status-canceled": ["Canceled", "취소됨"],
30 "enumdesc-status-canceled": ["Canceled status", "취소됨 상태"],
31
32 "enum-size-50": ["Small", "소형"],
33 "enumdesc-size-50": ["Small size", "소형 사이즈"],
34 "enum-size-100": ["Medium", "중형"],
35 "enumdesc-size-100": ["Medium size", "중형 사이즈"],
36 "enum-size-200": ["Large", "대형"],
37 "enumdesc-size-200": ["Large size", "큰 사이즈"],
38
39 "enum-toppings-fruitRings": ["Fruit Rings", "과일 링"],
40 "enumdesc-toppings-fruitRings": ["Fruit Rings", "과일 링"],
41 "enum-toppings-oreo": ["Oreo", "오레오"],
42 "enumdesc-toppings-oreo": ["Oreo", "오레오"],
43 "enum-toppings-strawberry": ["Strawberry", "딸기"],
44 "enumdesc-toppings-strawberry": ["Strawberry", "딸기"],
45 "enum-toppings-mango": ["Mango", "망고"],
46 "enumdesc-toppings-mango": ["Mango", "망고"],
47 "enum-toppings-cheeseCube": ["Cheese Cube", "치즈 큐브"],
48 "enumdesc-toppings-cheeseCube": ["Cheese Cube", "치즈 큐브"],
49 "enum-toppings-corn": ["Corn", "옥수수"],
50 "enumdesc-toppings-corn": ["Corn", "옥수수"],
51 "enum-toppings-granola": ["Granola", "그래놀라"],
52 "enumdesc-toppings-granola": ["Granola", "그래놀라"],
53 "enum-toppings-banana": ["Banana", "바나나"],
54 "enumdesc-toppings-banana": ["Banana", "바나나"],
55 "enum-toppings-fig": ["Fig", "무화과"],
56 "enumdesc-toppings-fig": ["Fig", "무화과"],
57 // * ==================== Etc ==================== * //
58
59 // ...other code
1
2// File: apps/koyo/lib/icecreamOrder/IcecreamOrder.Template.tsx
3"use client";
4import { cnst, st, usePage } from "@koyo/client";
5import { Layout, Field } from "@akanjs/ui";
6
7interface IcecreamOrderEditProps {
8 className?: string;
9}
10
11export const General = ({ className }: IcecreamOrderEditProps) => {
12 const icecreamOrderForm = st.use.icecreamOrderForm();
13 const { l } = usePage();
14 return (
15 <Layout.Template className={className}>
16 <Field.ToggleSelect
17 model="icecreamOrder"
18 field="size"
19 label={l.field("icecreamOrder", "size")}
20 items={[50, 100, 200] as const}
21 value={icecreamOrderForm.size}
22 onChange={st.do.setSizeOnIcecreamOrder}
23 />
24 <Field.MultiToggleSelect
25 model="icecreamOrder"
26 field="toppings"
27 label={l.field("icecreamOrder", "toppings")}
28 items={cnst.Topping}
29 value={icecreamOrderForm.toppings}
30 onChange={st.do.setToppingsOnIcecreamOrder}
31 />
32 </Layout.Template>
33 );
34};
1
2// File: apps/koyo/lib/icecreamOrder/IcecreamOrder.Unit.tsx
3import { clsx, ModelProps } from "@akanjs/client";
4import { cnst, usePage } from "@koyo/client";
5
6export const Card = ({ icecreamOrder }: ModelProps<"icecreamOrder", cnst.LightIcecreamOrder>) => {
7 const { l } = usePage();
8 return (
9 <div className="group flex h-36 w-full overflow-hidden rounded-xl bg-gradient-to-br from-pink-100 via-yellow-50 to-pink-200 px-8 py-6 shadow-md transition-all duration-300 hover:shadow-xl">
10 <div className="flex w-full flex-col justify-center">
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="mt-4 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 </div>
34 );
35};
1
2// File: apps/koyo/app/[lang]/icecreamOrder/page.tsx
3import { Load, Model } from "@akanjs/ui";
4import { cnst, fetch, IcecreamOrder, usePage } from "@koyo/client";
5
6export default function Page() {
7 const { l } = usePage();
8 return (
9 <Load.Page
10 of={Page}
11 loader={async () => {
12 const { icecreamOrderInitInPublic } = await fetch.initIcecreamOrderInPublic();
13 const icecreamOrderForm: Partial<cnst.IcecreamOrderInput> = {};
14 return { icecreamOrderInitInPublic, icecreamOrderForm };
15 }}
16 render={({ icecreamOrderInitInPublic, icecreamOrderForm }) => {
17 return (
18 <div>
19 <div className="flex items-center gap-4 pb-5 text-5xl font-black">
20 {l("icecreamOrder.modelName")}
21 <Model.New
22 className="btn btn-primary"
23 sliceName="icecreamOrderInPublic"
24 renderTitle="name"
25 partial={icecreamOrderForm}
26 >
27 <IcecreamOrder.Template.General />
28 </Model.New>
29 </div>
30 <IcecreamOrder.Zone.Card className="space-y-2" init={icecreamOrderInitInPublic} />
31 </div>
32 );
33 }}
34 />
35 );
36}