
akan create-application koyoakan 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 UI1import { enumOf, Int } from "@akanjs/base";
2import { via } from "@akanjs/constant";
3
4export class IcecreamOrderStatus extends enumOf("icecreamOrderStatus", [
5 "active",
6 "processing",
7 "served",
8 "finished",
9 "canceled",
10] as const) {}
11
12export class Topping extends enumOf("topping", [
13 "fruitRings",
14 "oreo",
15 "strawberry",
16 "mango",
17 "cheeseCube",
18 "corn",
19 "granola",
20 "banana",
21 "fig",
22] as const) {}
23
24export class IcecreamOrderInput extends via((field) => ({
25 size: field(Int),
26 toppings: field([Topping]),
27})) {}
28
29export class IcecreamOrderObject extends via(IcecreamOrderInput, (field) => ({
30 status: field(IcecreamOrderStatus, { default: "active" }),
31})) {}
32
33export class LightIcecreamOrder extends via(
34 IcecreamOrderObject,
35 ["size", "toppings", "status"] as const,
36 (resolve) => ({})
37) {}
38
39export class IcecreamOrder extends via(IcecreamOrderObject, LightIcecreamOrder, (resolve) => ({})) {}
40
41export class IcecreamOrderInsight extends via(IcecreamOrder, (field) => ({})) {}1import { modelDictionary } from "@akanjs/dictionary";
2
3import type { IcecreamOrder, IcecreamOrderInsight, IcecreamOrderStatus, Topping } from "./icecreamOrder.constant";
4import type { IcecreamOrderFilter } from "./icecreamOrder.document";
5import type { IcecreamOrderEndpoint, IcecreamOrderSlice } from "./icecreamOrder.signal";
6
7export const dictionary = modelDictionary(["en", "ko"])
8 .of((t) =>
9 t(["Icecream Order", "아이스크림 주문"]).desc([
10 "IcecreamOrder is an option that customers can choose when ordering icecream at koyo store.",
11 "아이스크림 주문은 koyo 가게에서 고객이 아이스크림을 주문할 때 커스텀할 수 있는 옵션들을 선택할 수 있습니다.",
12 ])
13 )
14 .model<IcecreamOrder>((t) => ({
15 size: t(["Size", "사이즈"]).desc(["Size of the icecream order", "아이스크림 주문의 사이즈"]),
16 toppings: t(["Toppings", "토핑"]).desc(["Toppings of the icecream order", "아이스크림 주문의 토핑"]),
17 status: t(["Status", "상태"]).desc(["Status of the icecream order", "아이스크림 주문의 상태"]),
18 }))
19 .enum<IcecreamOrderStatus>("icecreamOrderStatus", (t) => ({
20 active: t(["Active", "활성"]).desc(["Active status of the icecream order", "아이스크림 주문의 활성 상태"]),
21 processing: t(["Processing", "처리중"]).desc([
22 "Processing status of the icecream order",
23 "아이스크림 주문의 처리중 상태",
24 ]),
25 served: t(["Served", "서빙완료"]).desc(["Served status of the icecream order", "아이스크림 주문의 서빙완료 상태"]),
26 finished: t(["Finished", "완료"]).desc(["Finished status of the icecream order", "아이스크림 주문의 완료 상태"]),
27 canceled: t(["Canceled", "취소"]).desc(["Canceled status of the icecream order", "아이스크림 주문의 취소 상태"]),
28 }))
29 .enum<Topping>("topping", (t) => ({
30 fruitRings: t(["Fruit Rings", "과일 링"]).desc(["Fruit Rings topping", "과일 링 토핑"]),
31 oreo: t(["Oreo", "오레오"]).desc(["Oreo topping", "오레오 토핑"]),
32 strawberry: t(["Strawberry", "딸기"]).desc(["Strawberry topping", "딸기 토핑"]),
33 mango: t(["Mango", "망고"]).desc(["Mango topping", "망고 토핑"]),
34 cheeseCube: t(["Cheese Cube", "치즈 큐브"]).desc(["Cheese Cube topping", "치즈 큐브 토핑"]),
35 corn: t(["Corn", "옥수수"]).desc(["Corn topping", "옥수수 토핑"]),
36 granola: t(["Granola", "그래놀라"]).desc(["Granola topping", "그래놀라 토핑"]),
37 banana: t(["Banana", "바나나"]).desc(["Banana topping", "바나나 토핑"]),
38 fig: t(["Fig", "피그"]).desc(["Fig topping", "피그 토핑"]),
39 }))
40 .insight<IcecreamOrderInsight>((t) => ({}))
41 .slice<IcecreamOrderSlice>((fn) => ({
42 inPublic: fn(["IcecreamOrder In Public", "IcecreamOrder 공개"]).arg((t) => ({})),
43 }))
44 .endpoint<IcecreamOrderEndpoint>((fn) => ({}))
45 .error({})
46 .translate({});1"use client";
2import { cnst, st, usePage } from "@koyo/client";
3import { Layout, Field } from "@akanjs/ui";
4
5interface IcecreamOrderEditProps {
6 className?: string;
7}
8
9export const General = ({ className }: IcecreamOrderEditProps) => {
10 const icecreamOrderForm = st.use.icecreamOrderForm();
11 const { l } = usePage();
12 return (
13 <Layout.Template className={className}>
14 <Field.ToggleSelect
15 label={l("icecreamOrder.size")}
16 items={[50, 100, 200].map((size) => ({ label: `${size}cc`, value: size }))}
17 value={icecreamOrderForm.size}
18 onChange={st.do.setSizeOnIcecreamOrder}
19 />
20 <Field.MultiToggleSelect
21 label={l("icecreamOrder.toppings")}
22 items={cnst.Topping}
23 value={icecreamOrderForm.toppings}
24 onChange={st.do.setToppingsOnIcecreamOrder}
25 />
26 </Layout.Template>
27 );
28};1import { clsx, ModelProps } from "@akanjs/client";
2import { cnst, usePage } from "@koyo/client";
3
4export const Card = ({ icecreamOrder }: ModelProps<"icecreamOrder", cnst.LightIcecreamOrder>) => {
5 const { l } = usePage();
6 return (
7 <div className="group flex h-36 w-full overflow-hidden rounded-xl bg-linear-to-br from-pink-100 via-yellow-50 to-pink-200 px-8 py-6 shadow-md transition-all duration-300 hover:shadow-xl">
8 <div className="flex w-full flex-col justify-center">
9 <div className="flex items-center gap-2 text-lg font-semibold text-pink-700">
10 <span className="inline-block rounded bg-pink-200 px-2 py-1 text-xs font-bold tracking-wider uppercase">
11 {l("icecreamOrder.id")}
12 </span>
13 <span className="ml-2 font-mono text-pink-900">#{icecreamOrder.id.slice(-4)}</span>
14 </div>
15 <div className="mt-4 flex items-center gap-2">
16 <span className="inline-block rounded bg-yellow-200 px-2 py-1 text-xs font-bold tracking-wider text-yellow-800 uppercase">
17 {l("icecreamOrder.status")}
18 </span>
19 <span
20 className={clsx("ml-2 rounded-full px-3 py-1 text-sm font-semibold", {
21 "bg-green-100 text-green-700": icecreamOrder.status === "active",
22 "bg-blue-100 text-blue-700": icecreamOrder.status === "processing",
23 "bg-red-100 text-red-700": icecreamOrder.status === "served",
24 "bg-purple-100 text-purple-700": icecreamOrder.status === "finished",
25 "bg-gray-100 text-gray-700": icecreamOrder.status === "canceled",
26 })}
27 >
28 {l(`icecreamOrderStatus.${icecreamOrder.status}`)}
29 </span>
30 </div>
31 </div>
32 </div>
33 );
34};1import { Load, Model } from "@akanjs/ui";
2import { cnst, fetch, IcecreamOrder, usePage } from "@koyo/client";
3
4export default function Page() {
5 const { l } = usePage();
6 return (
7 <Load.Page
8 of={Page}
9 loader={async () => {
10 const { icecreamOrderInitInPublic } = await fetch.initIcecreamOrderInPublic();
11 const icecreamOrderForm: Partial<cnst.IcecreamOrderInput> = {};
12 return { icecreamOrderInitInPublic, icecreamOrderForm };
13 }}
14 render={({ icecreamOrderInitInPublic, icecreamOrderForm }) => {
15 return (
16 <div>
17 <div className="flex items-center gap-4 pb-5 text-5xl font-black">
18 {l("icecreamOrder.modelName")}
19 <Model.New
20 className="btn btn-primary"
21 sliceName="icecreamOrderInPublic"
22 renderTitle="name"
23 partial={icecreamOrderForm}
24 >
25 <IcecreamOrder.Template.General />
26 </Model.New>
27 </div>
28 <IcecreamOrder.Zone.Card className="space-y-2" init={icecreamOrderInitInPublic} />
29 </div>
30 );
31 }}
32 />
33 );
34}