@akanjs/cli 0.0.134 → 0.0.136

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cjs/index.js CHANGED
@@ -459,6 +459,11 @@ var Logger = class _Logger {
459
459
  }
460
460
  };
461
461
 
462
+ // pkgs/@akanjs/common/lowerlize.ts
463
+ var lowerlize = (str) => {
464
+ return str.charAt(0).toLowerCase() + str.slice(1);
465
+ };
466
+
462
467
  // pkgs/@akanjs/common/sleep.ts
463
468
  var sleep = async (ms) => {
464
469
  return new Promise((resolve) => {
@@ -2108,6 +2113,9 @@ var getArgumentValue = async (argMeta, value, workspace) => {
2108
2113
  throw new Error(`Invalid system type: ${argMeta.type}`);
2109
2114
  };
2110
2115
  var runCommands = async (...commands) => {
2116
+ process.on("unhandledRejection", (error) => {
2117
+ console.error(import_chalk2.default.red("[fatal]"), error);
2118
+ });
2111
2119
  const hasPackageJson = import_fs7.default.existsSync(`${__dirname}/../package.json`);
2112
2120
  const version = hasPackageJson ? JSON.parse(import_fs7.default.readFileSync(`${__dirname}/../package.json`, "utf8")).version : "0.0.1";
2113
2121
  import_commander.program.version(version).description("Akan CLI");
@@ -2518,1031 +2526,6 @@ var import_vite_tsconfig_paths = __toESM(require("vite-tsconfig-paths"));
2518
2526
 
2519
2527
  // pkgs/@akanjs/cli/src/module/module.prompt.ts
2520
2528
  var frameworkDescription = `
2521
- \uB098\uB294 \uC880 \uB354 \uD6A8\uC728\uC801\uC73C\uB85C \uCF54\uB529\uC744 \uD558\uAE30 \uC704\uD574\uC11C \uC790\uCCB4 \uD504\uB808\uC784\uC6CC\uD06C\uB97C \uC81C\uC791\uD588\uC5B4.
2522
-
2523
- \uADF8\uB798\uC11C \uC6B0\uB9AC \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uC124\uBA85\uC744 \uD574\uC904\uD14C\uB2C8\uAE4C \uC798 \uC774\uD574\uD558\uB3C4\uB85D \uD574. \uC6B0\uB9AC \uD504\uB808\uC784\uC6CC\uD06C\uB294 next.js 13\uACFC nest.js, capacitor.js, nx, mongoDB \uAE30\uBC18\uC758 \uD504\uB860\uD2B8\uC5D4\uB4DC, \uC571, \uBC31\uC5D4\uB4DC \uD1B5\uD569 \uD504\uB808\uC784\uC6CC\uD06C\uC57C. \uADF8\uB798\uC11C \uBC31\uC5D4\uB4DC, \uD504\uB860\uD2B8\uC5D4\uB4DC, DB Schema\uC5D0 \uB300\uD55C \uCF54\uB4DC\uAC00 \uBAA8\uB450 \uD55C \uD3F4\uB354 \uC548\uC5D0 \uC788\uC5B4. \uADF8\uB798\uC11C \uC790\uBC14\uC2A4\uD06C\uB9BD\uD2B8, \uD0C0\uC785\uC2A4\uD06C\uB9BD\uD2B8, \uADF8\uB9AC\uACE0 \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uC774\uD574\uB9CC \uC788\uB2E4\uBA74 \uAD6C\uBD84\uC9D3\uC9C0 \uC54A\uACE0 \uC0AC\uC6A9\uD560 \uC218 \uC788\uB2E4\uB294 \uC7A5\uC810\uC774 \uC788\uC5B4.
2524
-
2525
-
2526
-
2527
- \uAC00\uC7A5 \uC678\uBD80\uC758 \uAD6C\uC870\uB294
2528
-
2529
- - app
2530
-
2531
- - project1
2532
-
2533
- - project2
2534
-
2535
- - project3
2536
-
2537
- - project4
2538
-
2539
- - lib
2540
-
2541
- - core
2542
-
2543
- - external
2544
-
2545
- - game
2546
-
2547
- - mint
2548
-
2549
- - platform
2550
-
2551
- - shared
2552
-
2553
- - social
2554
-
2555
- - util
2556
-
2557
- app\uC740 \uAC01 \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4DC\uB294 \uACF3\uC774\uC57C
2558
-
2559
- lib \uD558\uC704 \uD3F4\uB354\uB4E4\uC740 \uC5EC\uB7EC \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uACF5\uC6A9\uC73C\uB85C \uC0AC\uC6A9\uD560 \uC218 \uC788\uACA0\uB2E4 \uB77C\uACE0 \uD310\uB2E8\uD574\uC11C \uBD84\uB9AC\uD574\uB193\uC740 \uACF3\uC774\uC57C.
2560
-
2561
- \uB2E4\uC74C\uC740 app/project \uB0B4\uBD80\uC758 \uAD6C\uC870\uB97C \uC54C\uB824\uC904\uAC8C.
2562
-
2563
-
2564
-
2565
- project
2566
-
2567
- - app
2568
-
2569
- - [lang]
2570
-
2571
- - (projectName)
2572
-
2573
- - (user)
2574
-
2575
- - page.tsx
2576
-
2577
- - layout.tsx
2578
-
2579
- - (public)
2580
-
2581
- - page.tsx
2582
-
2583
- - layout.tsx
2584
-
2585
- - (admin)
2586
-
2587
- - page.tsx
2588
-
2589
- - layout.tsx
2590
-
2591
-
2592
-
2593
- - lib
2594
-
2595
- - dataName1
2596
-
2597
- - dataName2
2598
-
2599
- - dataName3
2600
-
2601
- - dataName4
2602
-
2603
- - dataName5
2604
-
2605
- - dataName6
2606
-
2607
- - dataName7
2608
-
2609
-
2610
-
2611
- app\uC740 \uC9C1\uC811\uC801\uC73C\uB85C \uC720\uC800\uAC00 \uBCF4\uB294 \uD398\uC774\uC9C0\uB97C \uC9DC\uB294 \uACF3\uC774\uC57C. \uD3F4\uB354 \uAD6C\uC870\uB294 next 13\uC758 app directory\uB97C \uB530\uB974\uACE0 \uC788\uC5B4.
2612
-
2613
- public/page.tsx\uAC00 \uAC00\uC7A5 \uCD5C\uCD08\uB85C \uC811\uADFC\uB418\uB294 Index \uD398\uC774\uC9C0\uAC00 \uB420 \uAC70\uC57C.
2614
-
2615
- \uC544\uB798\uB294 public/page.tsx\uC758 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uC18C\uC2A4\uCF54\uB4DC\uC57C.
2616
-
2617
- \`\`\`
2618
-
2619
- import {{ Image, Link }} from "@util/ui";
2620
-
2621
- import {{ getSelf }} from "@akanjs/client";
2622
-
2623
-
2624
-
2625
- export default function Page() {{
2626
-
2627
- const self = getSelf();
2628
-
2629
- return (
2630
-
2631
- <div className="relative w-full h-screen overflow-hidden flex items-center justify-center">
2632
-
2633
- <Image
2634
-
2635
- className="absolute left-0 right-0 top-0 bottom-0 w-full h-screen -z-50"
2636
-
2637
- width={{1920}}
2638
-
2639
- height={{1080}}
2640
-
2641
- src="/back.jpg"
2642
-
2643
- />
2644
-
2645
- <div className="max-w-md bg-base-100/50 shadow-lg rounded-xl backdrop-blur-xs w-full py-8 px-16 flex flex-col items-center justify-center gap-3">
2646
-
2647
- <h1 className="text-4xl mt-2"><%= project %></h1>
2648
-
2649
- <h2 className="text-lg"><%= project %> description</h2>
2650
-
2651
- <Link className="w-full" href={{self ? "/home" : "/signin"}}>
2652
-
2653
- <button className="btn w-full btn-primary">Go to dashboard</button>
2654
-
2655
- </Link>
2656
-
2657
- </div>
2658
-
2659
- </div>
2660
-
2661
- );
2662
-
2663
- }}
2664
-
2665
- \`\`\`
2666
-
2667
-
2668
-
2669
- lib\uC740 \uC11C\uBE44\uC2A4\uB97C \uB9CC\uB4E4\uB2E4\uBCF4\uBA74 \uD544\uC694\uD55C \uB370\uC774\uD130\uB97C \uC815\uC758\uD558\uB294 \uACF3\uC774\uC57C.
2670
-
2671
- \uB530\uB77C\uC11C dataName\uC740 DB\uC774\uB984. \uC989 \uB370\uC774\uD130\uBA85\uC744 \uB530\uB77C. \uB2E4\uB9CC \uD3F4\uB354 \uB0B4\uBD80\uC5D0\uB294 backend, frontend\uC5D0\uC11C \uD1B5\uD569\uC73C\uB85C \uC4F0\uB294 \uC18C\uC2A4\uCF54\uB4DC\uB4E4\uC774 \uC788\uC5B4.
2672
-
2673
-
2674
-
2675
- \uC6B0\uB9AC \uD504\uB808\uC784\uC6CC\uD06C\uC758 \uD575\uC2EC\uC740 \uC815\uC758\uD55C \uBA54\uD0C0\uB370\uC774\uD130\uB4E4\uC744 \uC774\uC6A9\uD574\uC11C \uAC01 \uB370\uC774\uD130\uAC04\uC758 \uC5F0\uAD00\uC810\uC774\uB098, \uB370\uC774\uD130 \uD0C0\uC785\uC744 \uBCF4\uACE0 \uBBF8\uB9AC \uC815\uB9AC\uB41C \uACB0\uACFC\uB97C \uB9CC\uB4E4\uC5B4\uC8FC\uACE0 \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E0\uB2E4\uBA74 \uAC70\uC758 \uBAA8\uB450 \uD544\uC694\uD55C \uC791\uC5C5\uB4E4\uC744 \uBBF8\uB9AC \uC815\uB9AC\uD574\uB1A8\uC5B4. \uC608\uB97C \uB4E4\uC5B4 \uB370\uC774\uD130 \uD0C0\uC785\uC5D0 \uC774\uBBF8\uC9C0 \uD30C\uC77C\uC774 \uC788\uB2E4\uACE0 \uD55C\uB2E4\uBA74 \uBC31\uC5D4\uB4DC\uB97C \uD1B5\uD574 \uADF8 \uC774\uBBF8\uC9C0\uB97C \uC11C\uBC84\uC758 \uC2A4\uD1A0\uB9AC\uC9C0\uC5D0 \uC800\uC7A5\uD558\uACE0 \uADF8 \uC800\uC7A5\uB41C \uD30C\uC77C\uC744 \uD2B9\uC815 db\uC5D0 id\uB97C \uCD94\uAC00\uD574\uC8FC\uB294 \uD568\uC218\uB4E4 \uAC19\uC740 \uAC83\uB4E4\uC740 \uC5B4\uB290 \uD504\uB85C\uC81D\uD2B8\uB358 \uAC04\uC5D0 \uBAA8\uB450 \uC0AC\uC6A9\uB418\uB294 \uBC29\uC2DD\uC774\uB77C\uACE0 \uD310\uB2E8\uD588\uC5B4. \uD558\uC9C0\uB9CC \uC6B0\uB9AC\uB294 \uD504\uB85C\uC81D\uD2B8\uB97C \uC791\uC5C5\uD560 \uB54C\uB9C8\uB2E4 \uC774 \uBD88\uD544\uC694\uD55C \uC791\uC5C5\uB4E4\uC744 \uD56D\uC0C1 \uB9CC\uB4E4\uACE0 \uD14C\uC2A4\uD2B8\uB97C \uD558\uB294 \uBD88\uD544\uC694\uD55C \uC77C\uB4E4\uC744 \uD558\uACE0\uC788\uC9C0. \uADF8\uB798\uC11C \uC6B0\uB9AC\uB294 \uAC01 \uD504\uB85C\uC81D\uD2B8\uAC00 \uB9CC\uB4E4 \uB54C\uB9C8\uB2E4 \uD544\uC218\uB85C \uC788\uC5B4\uC57C\uD560 \uC774\uBBF8\uC9C0 \uC5C5\uB85C\uB4DC \uD504\uB85C\uC138\uC2A4\uB97C \uD504\uB808\uC784\uC6CC\uD06C\uB97C \uD1B5\uD574 \uBBF8\uB9AC \uC815\uC758\uB41C \uC0C1\uD0DC\uB85C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC5B4.
2676
-
2677
- \uACB0\uACFC\uC801\uC73C\uB85C \uC6B0\uB9B0 \uD544\uC694\uD558\uC9C0\uB9CC \uB9E4\uBC88 \uC791\uC5C5\uD558\uAE30\uC5D4 \uC190\uC774 \uB9CE\uC774\uAC00\uB294 \uC791\uC5C5\uB4E4\uC744 \uBBF8\uB9AC \uC815\uC758\uD574\uB1A8\uC5B4.
2678
-
2679
-
2680
-
2681
- \uB610\uD55C \uC6B0\uB9B0 CLI\uB97C \uD1B5\uD574\uC11C \uD2B9\uC815 \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354 \uD15C\uD50C\uB9BF\uC744 \uB9CC\uB4E4\uC5B4\uC8FC\uAC70\uB098, \uD2B9\uC815 \uBAA8\uB378\uC758 \uD544\uB4DC\uB4E4\uC744 \uB9CC\uB4E4 \uC218 \uC788\uB3C4\uB85D \uD574\uB1A8\uC5B4. Model\uC774\uB77C\uB294 \uBCC0\uC218\uC758 \uACB0\uACFC\uAC12\uC744 \uBC1B\uACE0 \uC800 \uD15C\uD50C\uB9BF\uC5D0 \uB9DE\uAC8C\uB054 \uC774\uB984\uC744 \uB123\uC5B4\uC8FC\uB294 \uAC70\uC9C0. \uB9CC\uC57D \uB0B4\uAC00 phone\uC774\uB77C\uB294 \uBCC0\uC218\uB97C \uB123\uC5C8\uB2E4\uBA74 \uC800 \uBAA8\uB378\uC5D0 Phone\uACFC \uAC19\uC740 \uACB0\uACFC\uAC12\uC73C\uB85C \uAC12\uC774 \uB4E4\uC5B4\uAC00\uAC8C \uB428\uC73C\uB85C\uC11C \uD15C\uD50C\uB9BF\uC774 \uC0DD\uC131\uB3FC. \uC774\uB294 \uC6B0\uB9AC\uAC00 \uC774\uBBF8 \uB9CC\uB4E4\uC5B4\uB193\uC740 \uCEE4\uB9E8\uB4DC\uAC00 \uC788\uC5B4.
2682
-
2683
-
2684
-
2685
- \uC608\uB97C \uB4E4\uC5B4 \uC5F0\uD544\uC774\uB77C\uB294 \uBAA8\uB378\uC744 \uCEE4\uB9E8\uB4DC\uB97C \uD1B5\uD574 \uC0DD\uC131\uD558\uBA74 \uC544\uB798\uC640 \uAC19\uC740 \uD30C\uC77C\uB4E4\uC774 \uC0DD\uC131\uB3FC.
2686
-
2687
-
2688
-
2689
- pencil
2690
-
2691
- - pencil.constant.ts ( pencil\uC5D0 \uB300\uD55C db schema, query, sort \uAD6C\uBB38\uC744 \uC815\uC758 )
2692
-
2693
- - pencil.document.ts ( model\uC5D0\uC11C \uC790\uC8FC \uC0AC\uC6A9\uD558\uB294 method, static function, middleware\uB85C \uAC04\uB2E8\uD55C db \uAD00\uB9AC \uD568\uC218\uB4F1\uC744 \uAD00\uB9AC\uD568.)
2694
-
2695
- - pencil.dictionary.ts (\uB2E4\uAD6D\uC5B4\uB97C \uC704\uD574\uC11C schema field, enum, service name\uC758 \uB2E4\uAD6D\uC5B4 \uC815\uBCF4 \uB4F1\uC744 \uBAA8\uB450 \uAD00\uB9AC. )
2696
-
2697
- - pencil.signal.ts (frontend\uC758 fetch\uB97C \uC5F4\uC5B4\uC8FC\uB294 \uC77C\uC885\uC758 \uCC3D\uAD6C, \uAD8C\uD55C\uAD00\uB9AC\uB4F1\uC744 \uD568 nestjs\uC758 resolver\uC640 \uC720\uC0AC )
2698
-
2699
- - pencil.service.ts (\uC2E4\uC9C8\uC801\uC778 \uC11C\uBE44\uC2A4 \uCF54\uB4DC\uB97C \uC791\uC131\uD558\uB294 \uACF3. \uAC01\uC885 \uBE44\uC988\uB2C8\uC2A4 \uB85C\uC9C1\uC744 \uCC98\uB9AC\uD568.)
2700
-
2701
- - pencil.store.ts (pencil field\uC5D0 \uB300\uD55C \uC0C1\uD0DC\uAD00\uB9AC \uC800\uC7A5\uC18C.)
2702
-
2703
- - pencil.Zone.tsx (\uC544\uB798 Templete, Unit, Util, View, Util\uB4F1\uC744 \uC870\uD569\uD574\uC11C \uB2E4\uC74C \uB808\uBCA8 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC791\uC131\uD558\uB294 \uACF3 client component\uB85C \uC815\uC758\uB428. )
2704
-
2705
- - pencil.Templete.tsx (\uC8FC\uB85C edit\uC5D0 \uC5F0\uAD00\uB41C \uCEF4\uD3EC\uB10C\uD2B8 \uC815\uC758\uD558\uB294 \uACF3. client component\uB85C \uC815\uC758\uB428. )
2706
-
2707
- - pencil.Unit.tsx (list \uCC98\uB7FC \uBCF5\uC218\uAC1C\uC758 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC815\uC758\uD558\uB294 \uACF3. server component\uB85C \uC815\uC758\uB428. )
2708
-
2709
- - pencil.Util.tsx (Action \uBC84\uD2BC\uC744 \uC815\uC758\uD558\uB294 \uACF3. client component\uB85C \uC815\uC758\uB428. )
2710
-
2711
- - pencil.View.tsx (\uB2E8\uC77C view \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC815\uC758\uD558\uB294 \uACF3. server component\uB85C \uC815\uC758\uB428. )
2712
-
2713
-
2714
-
2715
-
2716
- \uC5EC\uAE30\uC11C constant\uC5D0 \uC815\uC758 \uB418\uC5B4\uC788\uB294 schema\uB97C \uAE30\uBCF8\uC73C\uB85C \uD574\uC11C \uAC01 document, dictionary, signal, service, store\uB97C \uC0DD\uC131\uD574.
2717
-
2718
-
2719
-
2720
- document, signal, service\uB294 \uBC31\uC5D4\uB4DC\uC640 \uC5F0\uAD00\uB418\uC5B4\uC788\uB294 \uC18C\uC2A4\uCF54\uB4DC\uC57C.
2721
-
2722
- \uC11C\uB85C\uAC04\uC758 \uAD00\uACC4\uB294 \uC8FC\uB85C signal\uC5D0\uC11C \uAC00\uC7A5 \uBA3C\uC800 \uC694\uCCAD\uC744 \uBC1B\uACE0 \uC774\uD6C4 \uB85C\uC9C1\uC758 \uBCF5\uC7A1\uB3C4\uC5D0 \uB530\uB77C\uC11C service\uB85C \uCC98\uB9AC\uD560 \uC9C0, docuemnt\uB85C \uCC98\uB9AC\uD560 \uC9C0\uB97C \uACB0\uC815\uD574.
2723
-
2724
- signal - document
2725
-
2726
- signal - service - document
2727
-
2728
-
2729
-
2730
- store\uB294 \uD504\uB860\uD2B8\uC5D4\uB4DC\uC5D0\uC11C \uC0C1\uD0DC\uAD00\uB9AC\uB97C \uD560 \uB54C \uC0AC\uC6A9\uB3FC.
2731
-
2732
- \uC6B0\uB9AC\uB294 signal\uC758 \uCF54\uB4DC\uB85C \uB098\uC628 \uBA54\uD0C0\uB370\uC774\uD130\uB97C \uC774\uC6A9\uD574\uC11C store\uC5D0\uC11C \uD754\uD788 \uC0AC\uC6A9\uB420\uB9CC\uD55C \uD568\uC218\uB4E4\uC744 \uBBF8\uB9AC \uC815\uC758\uD558\uAC8C\uB054 \uB9CC\uB4E4\uC5C8\uC5B4.
2733
-
2734
-
2735
-
2736
- \uC774\uB807\uAC8C 11\uAC1C\uC758 \uD30C\uC77C\uB85C \uAD6C\uBD84\uD574\uC11C \uC815\uC758\uAC00 \uB3FC.
2737
-
2738
- \uC9C0\uAE08\uBD80\uD134 \uD55C \uD30C\uC77C\uB4E4\uB9C8\uB2E4 \uC0C1\uC138\uD55C \uC124\uBA85\uC744 \uD574\uC904\uAC8C.
2739
-
2740
-
2741
-
2742
- model.constant.ts
2743
-
2744
-
2745
-
2746
- constant\uB294 \uAE30\uBCF8 \uC2A4\uD0A4\uB9C8\uAC00 \uAD6C\uC131\uB418\uB294 \uACF3\uC774\uC57C. \uADF8\uB798\uC11C \uC774 \uACF3\uC5D0\uC11C DB \uC2A4\uD0A4\uB9C8, \uCFFC\uB9AC, \uC815\uB82C \uAD6C\uBB38\uC744 \uC815\uC758\uD574.
2747
-
2748
- \uC8FC\uC758\uD574\uC57C\uD560 \uAC74 decorator\uC5D0\uC11C \uC815\uC758\uD558\uB294 \uD0C0\uC785\uACFC \uC544\uB798\uC5D0 \uC815\uC758\uD558\uB294 \uD0C0\uC785\uC774 \uC57D\uAC04 \uB2E4\uB97C \uC218 \uC788\uC5B4
2749
-
2750
- \uC608\uB97C \uB4E4\uC5B4 decorator\uC5D0\uC11C\uB294 Int\uB85C \uC815\uC758\uD558\uACE0 constant\uC5D0\uC11C\uB294 number\uB85C \uC815\uC758\uD574\uC57C\uD574.
2751
-
2752
- \uCD94\uAC00\uB85C createdAt\uACFC updatedAt, status\uB294 \uAE30\uBCF8\uC801\uC73C\uB85C \uC0DD\uC131\uB418\uB294 \uD544\uB4DC\uC774\uB2C8\uAE4C \uB530\uB85C \uC815\uC758\uD558\uC9C0 \uC54A\uC544\uB3C4 \uB3FC.
2753
-
2754
- \uC774\uB294 decorator\uC5D0\uC11C\uB294 GraphQL\uC5D0\uC11C \uC0AC\uC6A9\uD560 \uD0C0\uC785\uC744 \uC815\uC758\uD558\uACE0 constant\uC5D0\uC11C\uB294 \uC2E4\uC81C \uD0C0\uC785\uC744 \uC815\uC758\uD558\uB294 \uAC70\uC9C0.
2755
-
2756
-
2757
-
2758
- \`\`\`
2759
-
2760
- import { enumOf, Int } from "@akanjs/base";
2761
-
2762
- import { Field, Filter, Model, sortOf, via } from "@akanjs/constant";
2763
-
2764
-
2765
-
2766
- export const \${dict.Model}Statuses = ["active"] as const;
2767
-
2768
- export type \${dict.Model}Status = (typeof \${dict.Model}Statuses)[number];
2769
-
2770
-
2771
-
2772
- //\uB370\uC774\uD130\uB97C \uB9CC\uB4E4 \uB54C \uD544\uC694\uD55C \uB370\uC774\uD130 \uD0C0\uC785\uC744 \uC815\uC758\uD558\uB294 \uACF3
2773
-
2774
- @Model.Input("\${dict.Model}Input")
2775
-
2776
- export class \${dict.Model}Input {{
2777
-
2778
- @Field.Prop(() => String)
2779
-
2780
- field: string;
2781
-
2782
-
2783
-
2784
- @Field.Prop(() => Int, {{ nullable: true }})
2785
-
2786
- fieldInt: number | null;
2787
-
2788
-
2789
-
2790
- @Field.Prop(() => Date, {{default: dayjs()}})
2791
-
2792
- fieldInt: Dayjs;
2793
-
2794
- }}
2795
-
2796
-
2797
-
2798
- //\uB370\uC774\uD130\uAC00 \uB9CC\uB4E4\uC5B4\uC9C4 \uC774\uD6C4 \uC0DD\uC131\uB418\uAC70\uB098 \uC800\uC7A5\uC73C\uB85C \uCD94\uAC00\uD560 \uC218 \uC788\uB294 \uACF3
2799
-
2800
- @Model.Object("\${dict.Model}Object")
2801
-
2802
- export class \${dict.Model}Object extends via(\${dict.Model}Input) {
2803
-
2804
- @Field.Prop(() => String, {{ enum: \${dict.Model}Statuses, default: "active" }})
2805
-
2806
- status: \${dict.Model}Status;
2807
-
2808
- }}
2809
-
2810
-
2811
-
2812
- //\uB370\uC774\uD130\uB97C \uB9AC\uC2A4\uD2B8\uB85C \uBCF4\uC5EC\uC904 \uB54C \uD544\uC694\uD55C \uB370\uC774\uD130\uB9CC \uC81C\uACF5\uD558\uB294 \uACF3
2813
-
2814
- @Model.Light("Light\${dict.Model}")
2815
-
2816
- export class Light\${dict.Model} extends via(\${dict.Model}Object, [
2817
-
2818
- "field",
2819
-
2820
- "status",
2821
-
2822
- ] as const) {}
2823
-
2824
-
2825
-
2826
- //\uCD5C\uC885 \uB370\uC774\uD130\uC758 Full \uD0C0\uC785\uC774 \uC815\uC758\uB418\uB294 \uACF3
2827
-
2828
- @Model.Full("\${dict.Model}")
2829
-
2830
- export class \${dict.Model} extends via(\${dict.Model}Object, Light\${dict.Model}) {}
2831
-
2832
-
2833
-
2834
- //\uB370\uC774\uD130\uC758 \uC778\uC0AC\uC774\uD2B8\uB97C \uCE21\uC815\uD558\uAE30 \uC704\uD574\uC11C \uD544\uC694\uD55C \uB370\uC774\uD130\uB97C \uC815\uC758\uD558\uB294 \uACF3
2835
-
2836
- @Model.Insight("\${dict.Model}Insight")
2837
-
2838
- export class \${dict.Model}Insight {{
2839
-
2840
- @Field.Prop(() => Int, {{ default: 0, accumulate: {{ $sum: 1 }} }})
2841
-
2842
- count: number;
2843
-
2844
- }}
2845
-
2846
-
2847
-
2848
- //\uB370\uC774\uD130\uC758 \uD1B5\uACC4\uB97C \uACC4\uC0B0\uD558\uB294 \uACF3
2849
-
2850
- @Model.Summary("\${dict.Model}Summary")
2851
-
2852
- export class \${dict.Model}Summary {{
2853
-
2854
- @Field.Prop(() => Int, {{ min: 0, default: 0, query: {{ status: {{}} }} }})
2855
-
2856
- total\${dict.Model}: number;
2857
-
2858
- }}
2859
-
2860
-
2861
-
2862
- @Model.Filter("\${dict.Model}Filter")
2863
-
2864
- export class \${dict.Model}Filter extends sortOf(\${dict.Model}, {}) {}
2865
-
2866
- \`\`\`
2867
-
2868
-
2869
-
2870
-
2871
- model.dictonary.ts
2872
-
2873
- \`\`\`
2874
-
2875
- import {
2876
-
2877
- baseTrans,
2878
-
2879
- getBaseSignalTrans,
2880
-
2881
- ModelDictionary,
2882
-
2883
- SignalDictionary,
2884
-
2885
- SummaryDictionary,
2886
-
2887
- } from "@akanjs/dictionary";
2888
-
2889
-
2890
-
2891
- import type { \${dict.Model}, \${dict.Model}Filter, \${dict.Model}Insight, \${dict.Model}Summary } from "./\${dict.model}.constant";
2892
-
2893
- import type { \${dict.Model}Signal } from "./\${dict.model}.signal";
2894
-
2895
-
2896
-
2897
- const modelDictionary = {{
2898
-
2899
- ...baseTrans,
2900
-
2901
- modelName: ["\${dict.Model}", "\${dict.Model}"],
2902
-
2903
- modelDesc: [
2904
-
2905
- "\${dict.Model} description",
2906
-
2907
- "\${dict.Model} \uC124\uBA85",
2908
-
2909
- ],
2910
-
2911
-
2912
-
2913
- // * ==================== Model ==================== * //
2914
-
2915
- field: ["Field", "\uD544\uB4DC"],
2916
-
2917
- "desc-field": ["Field", "\uD544\uB4DC"],
2918
-
2919
- // * ==================== Model ==================== * //
2920
-
2921
-
2922
-
2923
- // * ==================== Insight ==================== * //
2924
-
2925
- count: ["Count", "\uAC1C\uC218"],
2926
-
2927
- "desc-count": ["\${dict.Model} count in current query settting", "\uD604\uC7AC \uCFFC\uB9AC \uC124\uC815\uC5D0 \uB9DE\uB294 \${dict.Model} \uC218"],
2928
-
2929
- // * ==================== Insight ==================== * //
2930
-
2931
-
2932
-
2933
- // * ==================== Filter ==================== * //
2934
-
2935
- // * ==================== Filter ==================== * //
2936
-
2937
-
2938
-
2939
- // * ==================== Etc ==================== * //
2940
-
2941
- "enum-status-active": ["Active", "\uD65C\uC131"],
2942
-
2943
- "enumdesc-status-active": ["Active status", "\uD65C\uC131 \uC0C1\uD0DC"],
2944
-
2945
- // * ==================== Etc ==================== * //
2946
-
2947
- }} satisfies ModelDictionary<\${dict.Model}, \${dict.Model}Insight, typeof \${dict.Model}Sort>;
2948
-
2949
-
2950
-
2951
- export const \${dict.Model}SummaryDictionary = {{
2952
-
2953
- // * ==================== Summary ==================== * //
2954
-
2955
- total\${dict.Model}: ["Total \${dict.Model}", "\uCD1D \${dict.Model} \uC218"],
2956
-
2957
- "desc-total\${dict.Model}": ["Total \${dict.Model} count in the database", "\uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC5D0 \uC800\uC7A5\uB41C \uCD1D \${dict.Model} \uC218"],
2958
-
2959
- // * ==================== Summary ==================== * //
2960
-
2961
- }} satisfies SummaryDictionary<\${dict.Model}Summary>;
2962
-
2963
-
2964
-
2965
- const signalDictionary = {{
2966
-
2967
- ...getBaseSignalTrans("\${dict.Model}" as const),
2968
-
2969
- // * ==================== Endpoint ==================== * //
2970
-
2971
- "api-\${dict.Model}ListInPublic": ["\${dict.Model} List In Public", "\uACF5\uAC1C\uB41C \${dict.Model} \uB9AC\uC2A4\uD2B8"],
2972
-
2973
- "apidesc-\${dict.Model}ListInPublic": ["Get a list of public \${dict.Model}", "\uACF5\uAC1C\uB41C \${dict.Model}\uC758 \uB9AC\uC2A4\uD2B8\uB97C \uAC00\uC838\uC635\uB2C8\uB2E4"],
2974
-
2975
- "arg-\${dict.Model}ListInPublic-statuses": ["Statuses", "\uC0C1\uD0DC"],
2976
-
2977
- "argdesc-\${dict.Model}ListInPublic-statuses": ["Statuses to filter", "\uD544\uD130\uB9C1\uD560 \uC0C1\uD0DC"],
2978
-
2979
- "arg-\${dict.Model}ListInPublic-skip": ["Skip", "\uAC74\uB108\uB6F0\uAE30"],
2980
-
2981
- "argdesc-\${dict.Model}ListInPublic-skip": ["Number of items to skip", "\uAC74\uB108\uB6F8 \uC544\uC774\uD15C \uC218"],
2982
-
2983
- "arg-\${dict.Model}ListInPublic-limit": ["Limit", "\uC81C\uD55C"],
2984
-
2985
- "argdesc-\${dict.Model}ListInPublic-limit": ["Maximum number of items to return", "\uBC18\uD658\uD560 \uCD5C\uB300 \uC544\uC774\uD15C \uC218"],
2986
-
2987
- "arg-\${dict.Model}ListInPublic-sort": ["Sort", "\uC815\uB82C"],
2988
-
2989
- "argdesc-\${dict.Model}ListInPublic-sort": ["Sort order of the items", "\uC544\uC774\uD15C\uC758 \uC815\uB82C \uC21C\uC11C"],
2990
-
2991
-
2992
-
2993
- "api-\${dict.Model}InsightInPublic": ["\${dict.Model} Insight In Public", "\uACF5\uAC1C\uB41C \${dict.Model} \uC778\uC0AC\uC774\uD2B8"],
2994
-
2995
- "apidesc-\${dict.Model}InsightInPublic": [
2996
-
2997
- "Get insight data for public \${dict.Model}",
2998
-
2999
- "\uACF5\uAC1C\uB41C \${dict.Model}\uC5D0 \uB300\uD55C \uC778\uC0AC\uC774\uD2B8 \uB370\uC774\uD130\uB97C \uAC00\uC838\uC635\uB2C8\uB2E4",
3000
-
3001
- ],
3002
-
3003
- "arg-\${dict.Model}InsightInPublic-statuses": ["Statuses", "\uC0C1\uD0DC"],
3004
-
3005
- "argdesc-\${dict.Model}InsightInPublic-statuses": ["Statuses to filter", "\uD544\uD130\uB9C1\uD560 \uC0C1\uD0DC"],
3006
-
3007
- // * ==================== Endpoint ==================== * //
3008
-
3009
- }} satisfies SignalDictionary<\${dict.Model}Signal, \${dict.Model}>;
3010
-
3011
- \`\`\`
3012
-
3013
- export const \${dict.model}Dictionary = {{ ...modelDictionary, ...signalDictionary }};
3014
-
3015
-
3016
-
3017
-
3018
-
3019
- model.document.ts
3020
-
3021
- \`\`\`
3022
-
3023
- import { beyond, by, Database, into, type SchemaOf } from "@akanjs/document";
3024
-
3025
-
3026
-
3027
- import { cnst } from "../cnst";
3028
-
3029
- @Database.Input(() => cnst.\${dict.Model}Input)
3030
-
3031
- export class \${dict.Model}Input extends by(cnst.\${dict.Model}Input) {}
3032
-
3033
-
3034
-
3035
- @Database.Document(() => cnst.\${dict.Model})
3036
-
3037
- export class \${dict.Model} extends by(cnst.\${dict.Model}) {}
3038
-
3039
-
3040
-
3041
- @Database.Model(() => cnst.\${dict.Model})
3042
-
3043
- export class \${dict.Model}Model extends into(\${dict.Model}, cnst.\${dict.Model}Cnst) {
3044
-
3045
- async getSummary(): Promise<cnst.\${dict.Model}Summary> {
3046
-
3047
- return {
3048
-
3049
- ...(await this.getDefaultSummary()),
3050
-
3051
- };
3052
-
3053
- }
3054
-
3055
- }
3056
-
3057
-
3058
-
3059
- @Database.Middleware(() => cnst.\${dict.Model})
3060
-
3061
- export class \${dict.Model}Middleware extends beyond(\${dict.Model}Model, \${dict.Model}) {
3062
-
3063
- onSchema(schema: SchemaOf<\${dict.Model}Model, \${dict.Model}>) {
3064
-
3065
- // schema.index({ status: 1 })
3066
-
3067
- }
3068
-
3069
- }
3070
-
3071
- \`\`\`
3072
-
3073
-
3074
-
3075
-
3076
- model.signal.ts
3077
-
3078
- \`\`\`
3079
-
3080
- import { Int } from "@akanjs/base";
3081
-
3082
- import { SortOf } from "@akanjs/constant";
3083
-
3084
- import { Arg, DbSignal, Mutation, Query, resolve, Signal } from "@akanjs/signal";
3085
-
3086
-
3087
-
3088
- import { cnst, Srvs } from "../cnst";
3089
-
3090
-
3091
-
3092
- @Signal(() => cnst.\${dict.Model})
3093
-
3094
- export class \${dict.Model}Signal extends DbSignal(cnst.\${dict.model}Cnst, Srvs, {
3095
-
3096
- guards: { get: Query.Public, cru: Mutation.Public },
3097
-
3098
- }) {
3099
-
3100
- // * /////////////////////////////////////
3101
-
3102
- // * Public Slice
3103
-
3104
- @Query.Public(() => [cnst.\${dict.Model}])
3105
-
3106
- async \${dict.model}ListInPublic(
3107
-
3108
- @Arg.Query("statuses", () => [String], { nullable: true }) statuses: cnst.\${dict.Model}Status[] | null,
3109
-
3110
- @Arg.Query("skip", () => Int, { nullable: true }) skip: number | null,
3111
-
3112
- @Arg.Query("limit", () => Int, { nullable: true }) limit: number | null,
3113
-
3114
- @Arg.Query("sort", () => String, { nullable: true }) sort: SortOf<cnst.\${dict.Model}Filter> | null
3115
-
3116
- ) {
3117
-
3118
- const \${dict.models} = await this.\${dict.model}Service.listByStatuses(statuses, { skip, limit, sort });
3119
-
3120
- return resolve<cnst.\${dict.Model}[]>(\${dict.models});
3121
-
3122
- }
3123
-
3124
- @Query.Public(() => cnst.\${dict.Model}Insight)
3125
-
3126
- async \${dict.model}InsightInPublic(
3127
-
3128
- @Arg.Query("statuses", () => [String], { nullable: true }) statuses: cnst.\${dict.Model}Status[] | null
3129
-
3130
- ) {
3131
-
3132
- const \${dict.model}Insight = await this.\${dict.model}Service.insightByStatuses(statuses);
3133
-
3134
- return resolve<cnst.\${dict.Model}Insight>(\${dict.model}Insight);
3135
-
3136
- }
3137
-
3138
- // * Public Slice
3139
-
3140
- // * /////////////////////////////////////
3141
-
3142
- }
3143
-
3144
- \`\`\`
3145
-
3146
-
3147
-
3148
-
3149
- model.service.ts
3150
-
3151
- \`\`\`
3152
-
3153
- import { DbService, Service } from "@akanjs/service";
3154
-
3155
-
3156
-
3157
- import { cnst } from "../cnst";
3158
-
3159
- import * as db from "../db";
3160
-
3161
-
3162
-
3163
- @Service("\${dict.Model}Service")
3164
-
3165
- export class \${dict.Model}Service extends DbService(db.\${dict.Model}Db) {
3166
-
3167
- async summarize(): Promise<cnst.\${dict.Model}Summary> {
3168
-
3169
- return {
3170
-
3171
- ...(await this.\${dict.Model}Model.getSummary()),
3172
-
3173
- };
3174
-
3175
- }
3176
-
3177
- }
3178
-
3179
- \`\`\`
3180
-
3181
-
3182
-
3183
-
3184
- model.store.ts
3185
-
3186
- \`\`\`
3187
-
3188
- import { stateOf, Store } from "@akanjs/store";
3189
-
3190
-
3191
-
3192
- import { cnst } from "../cnst";
3193
-
3194
- import { fetch } from "../fetch";
3195
-
3196
-
3197
-
3198
- @Store(() => cnst.\${dict.Model})
3199
-
3200
- export class \${dict.Model}Store extends stateOf(fetch.\${dict.model}Gql, {
3201
-
3202
- // state
3203
-
3204
- }) {
3205
-
3206
- // action
3207
-
3208
- }
3209
-
3210
- \`\`\`
3211
-
3212
-
3213
-
3214
-
3215
- model.Zone.tsx
3216
-
3217
- \`\`\`
3218
-
3219
- "use client";
3220
-
3221
- import { Data, Load } from "@shared/ui";
3222
-
3223
- import { ModelsProps } from "@akanjs/client";
3224
-
3225
- import { cnst, \${dict.Model} } from "@\${dict.appName}/client";
3226
-
3227
- import { ClientInit, ClientView, DefaultOf } from "@akanjs/signal";
3228
-
3229
-
3230
-
3231
- export const Admin = ({ sliceName = "\${dict.model}", init, query }: ModelsProps<cnst.\${dict.Model}>>) => {
3232
-
3233
- return (
3234
-
3235
- <Data.ListContainer
3236
-
3237
- init={init}
3238
-
3239
- query={query}
3240
-
3241
- sliceName={sliceName}
3242
-
3243
- renderItem={\${dict.Model}.Unit.Card}
3244
-
3245
- renderDashboard={\${dict.Model}.Util.Stat}
3246
-
3247
- renderInsight={\${dict.Model}.Util.Insight}
3248
-
3249
- renderTemplate={\${dict.Model}.Template.General}
3250
-
3251
- renderTitle={(\${dict.model}: DefaultOf<cnst.\${dict.Model}>>) => \`\${dict.Model} - \${\${dict.model}.id ? \${dict.model}.id : "New"}\`}
3252
-
3253
- renderView={(\${dict.model}: cnst.\${dict.Model}>) => <\${dict.Model}.View.General \${dict.model}={\${dict.model}} />}
3254
-
3255
- columns={[
3256
-
3257
- "id",
3258
-
3259
- "status",
3260
-
3261
- "createdAt",
3262
-
3263
- "updatedAt",
3264
-
3265
- ]}
3266
-
3267
- actions={(\${dict.model}: cnst.Light\${dict.Model}, idx) => ["remove", "edit", "view"]}
3268
-
3269
- />
3270
-
3271
- );
3272
-
3273
- };
3274
-
3275
-
3276
-
3277
- interface CardProps {
3278
-
3279
- className?: string;
3280
-
3281
- init: ClientInit<"\${dict.model}", cnst.Light\${dict.Model}>;
3282
-
3283
- }
3284
-
3285
- export const Card = ({ className, init }: CardProps) => {
3286
-
3287
- return (
3288
-
3289
- <Load.Units
3290
-
3291
- className={className}
3292
-
3293
- init={init}
3294
-
3295
- renderItem={(\${dict.model}: cnst.Light\${dict.Model}) => (
3296
-
3297
- <\${dict.Model}.Unit.Card key={\${dict.model}.id} href={\`/\${dict.model}/\${\${dict.model}.id}\`} \${dict.model}={\${dict.model}} />
3298
-
3299
- )}
3300
-
3301
- />
3302
-
3303
- );
3304
-
3305
- };
3306
-
3307
-
3308
-
3309
- interface ViewProps {
3310
-
3311
- className?: string;
3312
-
3313
- view: ClientView<"\${dict.model}", cnst.\${dict.Model}>;
3314
-
3315
- }
3316
-
3317
- export const View = ({ view }: ViewProps) => {
3318
-
3319
- return <Load.View view={view} renderView={(\${dict.model}) => <\${dict.Model}.View.General \${dict.model}={\${dict.model}} />} />;
3320
-
3321
- };
3322
-
3323
- \`\`\`
3324
-
3325
-
3326
-
3327
- model.Templete.tsx
3328
-
3329
- \`\`\`
3330
-
3331
- "use client";
3332
-
3333
- import { cnst, st, usePage } from "@\${dict.appName}/client";
3334
-
3335
- import { Field } from "@shared/ui";
3336
-
3337
- import { Layout } from "@util/ui";
3338
-
3339
-
3340
-
3341
- interface \${dict.Model}EditProps {
3342
-
3343
- \${dict.model}Id?: string | null;
3344
-
3345
- }
3346
-
3347
-
3348
-
3349
- export const General = ({ \${dict.model}Id = undefined }: \${dict.Model}EditProps) => {
3350
-
3351
- const \${dict.model}Form = st.use.\${dict.model}Form();
3352
-
3353
- const { l } = usePage();
3354
-
3355
- return (
3356
-
3357
- <Layout.Template>
3358
-
3359
- <Field.Text
3360
-
3361
- label={l.field("\${dict.model}", "id")}
3362
-
3363
- desc={l.desc("\${dict.model}", "id")}
3364
-
3365
- value={\${dict.model}Form.id}
3366
-
3367
- onChange={st.do.setIdOn\${dict.Model}}
3368
-
3369
- />
3370
-
3371
- </Layout.Template>
3372
-
3373
- );
3374
-
3375
- };
3376
-
3377
- \`\`\`
3378
-
3379
-
3380
-
3381
-
3382
- model.Unit.tsx
3383
-
3384
- \`\`\`
3385
-
3386
- import { ModelProps } from "@akanjs/client";
3387
-
3388
- import { cnst, \${dict.Model} } from "@\${dict.appName}/client";
3389
-
3390
- import { Link } from "@util/ui";
3391
-
3392
-
3393
-
3394
- export const Card = ({ \${dict.model}, href }: ModelProps<"\${dict.model}", cnst.Light\${dict.Model}>>) => {
3395
-
3396
- return (
3397
-
3398
- <Link href={href} className="animate-fadeIn w-full h-36 flex rounded-lg shadow-sm hover:shadow-lg duration-300">
3399
-
3400
- <div>{\${dict.model}.id}</div>
3401
-
3402
- </Link>
3403
-
3404
- );
3405
-
3406
- };
3407
-
3408
- \`\`\`
3409
-
3410
-
3411
-
3412
-
3413
- model.Util.tsx
3414
-
3415
- \`\`\`
3416
-
3417
- "use client";
3418
-
3419
- import { ModelDashboardProps, ModelInsightProps } from "@akanjs/client";
3420
-
3421
- import { getQueryMap } from "@akanjs/constant";
3422
-
3423
- import { cnst } from "@\${dict.appName}/client";
3424
-
3425
- import { Data } from "@shared/ui";
3426
-
3427
-
3428
-
3429
- export const Stat = ({
3430
-
3431
- className,
3432
-
3433
- summary,
3434
-
3435
- sliceName = "\${dict.model}",
3436
-
3437
- queryMap = getQueryMap(cnst.\${dict.Model}Summary),
3438
-
3439
- hidePresents,
3440
-
3441
- }: ModelDashboardProps<cnst.Summary>) => {
3442
-
3443
- return (
3444
-
3445
- <Data.Dashboard
3446
-
3447
- className={className}
3448
-
3449
- summary={summary}
3450
-
3451
- sliceName={sliceName}
3452
-
3453
- queryMap={queryMap}
3454
-
3455
- columns={["total\${dict.Model}"]}
3456
-
3457
- hidePresents={hidePresents}
3458
-
3459
- />
3460
-
3461
- );
3462
-
3463
- };
3464
-
3465
-
3466
-
3467
- export const Insight = ({
3468
-
3469
- className,
3470
-
3471
- insight,
3472
-
3473
- sliceName = "\${dict.model}",
3474
-
3475
- }: ModelInsightProps<cnst.\${dict.Model}Insight>) => {
3476
-
3477
- return (
3478
-
3479
- <Data.Insight
3480
-
3481
- className={className}
3482
-
3483
- insight={insight}
3484
-
3485
- sliceName={sliceName}
3486
-
3487
- columns={["count"]}
3488
-
3489
- />
3490
-
3491
- );
3492
-
3493
- };
3494
-
3495
- \`\`\`
3496
-
3497
-
3498
-
3499
-
3500
- model.View.tsx
3501
-
3502
- \`\`\`
3503
-
3504
- import { clsx } from "@akanjs/client";
3505
-
3506
- import { cnst } from "@\${dict.appName}/client";
3507
-
3508
- import { Image } from "@util/ui";
3509
-
3510
-
3511
-
3512
- interface \${dict.Model}ViewProps {
3513
-
3514
- className?: string;
3515
-
3516
- \${dict.model}: cnst.\${dict.Model};
3517
-
3518
- self?: { id?: string } | null;
3519
-
3520
- }
3521
-
3522
-
3523
-
3524
- export const General = ({ className, \${dict.model}, self }: \${dict.Model}ViewProps) => {
3525
-
3526
- return (
3527
-
3528
- <div className={clsx(className, "animate-fadeIn w-full")}>
3529
-
3530
- <div>{\${dict.model}.id}</div>
3531
-
3532
- </div>
3533
-
3534
- );
3535
-
3536
- };
3537
-
3538
- \`\`\`
3539
-
3540
- \uC774\uB7F0\uC2DD\uC73C\uB85C \uC6B0\uB9AC\uAC00 \uC9C1\uC811 \uB9CC\uB4E0 \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uAE30\uBC18\uD574\uC11C db\uC640 \uC11C\uBE44\uC2A4 \uC2A4\uD1A0\uC5B4\uAE4C\uC9C0 \uBAA8\uB450 \uC5F0\uB3D9\uD574\uC11C \uD558\uB098\uC758 \uBAA8\uB378\uC5D0 \uB9DE\uAC8C\uB054 \uBAA8\uB4C8\uC744 \uD55C \uD3F4\uB354\uC5D0\uC11C \uC791\uC5C5\uD560 \uC218 \uC788\uAC8C\uB054 \uAD6C\uC870\uB97C \uB9CC\uB4E4\uC5C8\uC5B4.
3541
-
3542
-
3543
-
3544
- \uC77C\uB2E8 \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uC124\uBA85\uC740 \uC774 \uC815\uB3C4\uB85C \uD558\uACE0 \uC774 \uC815\uBCF4\uB4E4\uC744 \uAE30\uBC18\uC73C\uB85C \uB0B4\uAC00 \uC6D0\uD558\uB294 \uC694\uAD6C\uB97C \uB4E4\uC5B4\uC918.
3545
-
3546
2529
  `;
3547
2530
  var moduleDesription = `
3548
2531
 
@@ -3978,6 +2961,767 @@ var utilUiDescription = `
3978
2961
  - Modern React patterns (hooks, context, providers)
3979
2962
  - Error handling and validation systems
3980
2963
  `;
2964
+ var shardUiDescription = `
2965
+ Shared UI Library Analysis
2966
+
2967
+ Library Overview
2968
+
2969
+ The shared UI library in the akansoft project is a comprehensive React component collection designed for building data-driven
2970
+ applications. It provides a complete toolkit with consistent patterns, extensive customization options, and deep integration with the
2971
+ application's store system.
2972
+
2973
+ Key Characteristics
2974
+
2975
+ - Store-Centric Architecture: Deep integration with standardized store slices and naming conventions
2976
+ - Type-Safe Components: Extensive TypeScript generic usage for compile-time safety
2977
+ - Internationalization Ready: Built-in multi-language support throughout all components
2978
+ - Responsive Design: Mobile-first approach with adaptive breakpoints
2979
+ - Performance Optimized: Memoization, lazy loading, and efficient re-rendering
2980
+ - Accessibility Compliant: Proper ARIA attributes and keyboard navigation
2981
+ - Extensible Architecture: Plugin systems and customizable renderers
2982
+ - Unidirectional Data Flow: Predictable state management patterns
2983
+
2984
+ Component Categories
2985
+
2986
+ 1. Field Components (Field.tsx)
2987
+
2988
+ Field.tsx is a core form component library for the akansoft project, providing more than 25 specialized input fields.
2989
+
2990
+ ## Basic Structure and Common Features
2991
+
2992
+ ### Label Component
2993
+
2994
+ \`\`\`typescript
2995
+ interface LabelProps {
2996
+ className?: string;
2997
+ label: string;
2998
+ desc?: string;
2999
+ unit?: string;
3000
+ nullable?: boolean;
3001
+ mode?: "view" | "edit";
3002
+ }
3003
+ \`\`\`
3004
+ - Purpose: Consistent label display for all fields
3005
+ - Features:
3006
+ - Help tooltip support (desc)
3007
+ - Unit display (unit)
3008
+ - Optional field display (nullable)
3009
+ - Required indicator is commented out (design decision)
3010
+
3011
+ ## Text Input Components
3012
+
3013
+ ### 1. Field.Text
3014
+
3015
+ \`\`\`typescript
3016
+ interface TextProps {
3017
+ label?: string;
3018
+ desc?: string;
3019
+ value: string | null;
3020
+ onChange: (value: string) => void;
3021
+ placeholder?: string;
3022
+ nullable?: boolean;
3023
+ disabled?: boolean;
3024
+ minlength?: number; // Default: nullable ? 0 : 2
3025
+ maxlength?: number; // Default: 200
3026
+ transform?: (value: string) => string;
3027
+ validate?: (text: string) => boolean | string;
3028
+ cache?: boolean; // Form caching support
3029
+ onPressEnter?: () => void;
3030
+ inputStyleType?: "bordered" | "borderless" | "underline";
3031
+ }
3032
+ \`\`\`
3033
+ Key features:
3034
+ - Real-time transformation (transform)
3035
+ - Automatic cache key generation: \${label}-\${desc}-text
3036
+ - Length validation and custom validation
3037
+ - Various input styles supported
3038
+
3039
+ ### 2. Field.TextArea
3040
+
3041
+ \`\`\`typescript
3042
+ interface TextAreaProps {
3043
+ // Same basic props as Text +
3044
+ rows?: number; // Default: 3
3045
+ maxlength?: number; // Default: 1000
3046
+ }
3047
+ \`\`\`
3048
+ Key features:
3049
+ - Multi-line text input
3050
+ - Adjustable height (rows)
3051
+ - Longer maximum length than Text
3052
+
3053
+ ### 3. Field.Price (to be deprecated)
3054
+
3055
+ \`\`\`typescript
3056
+ interface PriceProps {
3057
+ // Similar to Text but specialized for prices
3058
+ maxlength?: number; // Default: 80
3059
+ placeholder?: string; // Default: "~ \${l('shared.priceUnit')}"
3060
+ }
3061
+ \`\`\`
3062
+ Key features:
3063
+ - Automatic removal of commas and spaces
3064
+ - Automatic price unit placeholder
3065
+ - Marked as "delete" in comments - legacy component
3066
+
3067
+ ## List Management Components
3068
+
3069
+ ### 4. Field.List
3070
+
3071
+ \`\`\`typescript
3072
+ interface ListProps<Item> {
3073
+ label?: string;
3074
+ value: Item[];
3075
+ onChange: (value: Item[]) => void;
3076
+ onAdd: () => void;
3077
+ renderItem: (item: Item, idx: number) => ReactNode;
3078
+ }
3079
+ \`\`\`
3080
+ Key features:
3081
+ - Generic type supporting all item types
3082
+ - Custom rendering for each item
3083
+ - Automatic delete button addition
3084
+ - Automatic divider insertion
3085
+
3086
+ ### 5. Field.TextList
3087
+
3088
+ \`\`\`typescript
3089
+ interface TextListProps {
3090
+ value: string[];
3091
+ onChange: (value: string[]) => void;
3092
+ minlength?: number; // Minimum array length, Default: 0
3093
+ maxlength?: number; // Maximum array length, Default: 50
3094
+ minTextlength?: number; // Minimum individual text length, Default: 2
3095
+ maxTextlength?: number; // Maximum individual text length, Default: 200
3096
+ transform?: (value: string) => string;
3097
+ validate?: (text: string) => boolean | string;
3098
+ cache?: boolean;
3099
+ }
3100
+ \`\`\`
3101
+ Key features:
3102
+ - Drag and drop for order changes
3103
+ - Individual text input validation
3104
+ - Cache key: \${label}-\${desc}-textList-[\${idx}]
3105
+ - Conditional "New" button display based on maximum length limit
3106
+
3107
+ ### 6. Field.Tags
3108
+
3109
+ \`\`\`typescript
3110
+ interface TagsProps {
3111
+ value: string[];
3112
+ onChange: (value: string[]) => void;
3113
+ minlength?: number; // Default: 0
3114
+ maxlength?: number; // Default: 50
3115
+ minTextlength?: number; // Default: 2
3116
+ maxTextlength?: number; // Default: 10 (tags are shorter)
3117
+ transform?: (value: string) => string;
3118
+ validate?: (text: string) => boolean | string;
3119
+ }
3120
+ \`\`\`
3121
+ Key features:
3122
+ - Tag style UI (includes # prefix)
3123
+ - Inline editing mode
3124
+ - Creates additional input field when clicked
3125
+ - Cancel editing with ESC key
3126
+ - Complete tag addition with Enter/Blur
3127
+
3128
+ ## Numeric Input Components
3129
+
3130
+ ### 7. Field.Number
3131
+
3132
+ \`\`\`typescript
3133
+ interface NumberProps {
3134
+ value: number | null;
3135
+ onChange: (value: number) => void;
3136
+ min?: number;
3137
+ max?: number;
3138
+ unit?: string;
3139
+ transform?: (value: number) => number;
3140
+ validate?: (text: number) => boolean | string;
3141
+ formatter?: (value: string) => string; // Display format
3142
+ parser?: (value: string) => string; // Parsing format
3143
+ cache?: boolean;
3144
+ }
3145
+ \`\`\`
3146
+ Key features:
3147
+ - Automatic number validation and min/max checks
3148
+ - Customizable display format with formatter/parser
3149
+ - Unit display support
3150
+ - Cache key: \${label}-\${desc}-number
3151
+
3152
+ ### 8. Field.DoubleNumber
3153
+
3154
+ \`\`\`typescript
3155
+ interface DoubleNumberProps {
3156
+ value: [number, number] | null;
3157
+ onChange: (value: [number, number]) => void;
3158
+ min?: [number, number] | null;
3159
+ max?: [number, number] | null;
3160
+ separator?: ReactNode | string; // Separator between inputs
3161
+ }
3162
+ \`\`\`
3163
+ Key features:
3164
+ - Range input (start-end values)
3165
+ - Independent min/max validation for each
3166
+ - Custom separator (e.g., "~", "-")
3167
+ - Cache keys: \${label}-\${desc}-number-[0], \${label}-\${desc}-number-[1]
3168
+
3169
+ ## Selection Components
3170
+
3171
+ ### 9. Field.Switch
3172
+
3173
+ \`\`\`typescript
3174
+ interface SwitchProps {
3175
+ value: boolean;
3176
+ onChange: (value: boolean) => void;
3177
+ onDesc?: string; // Description when true
3178
+ offDesc?: string; // Description when false
3179
+ disabled?: boolean;
3180
+ }
3181
+ \`\`\`
3182
+ Key features:
3183
+ - DaisyUI toggle style
3184
+ - State-specific description text
3185
+ - Uses toggle-accent class
3186
+
3187
+ ### 10. Field.ToggleSelect
3188
+
3189
+ \`\`\`typescript
3190
+ interface ToggleSelectProps<I> {
3191
+ items: { label: string; value: I }[] | readonly I[] | I[] | Enum<I>;
3192
+ value: I | null;
3193
+ onChange: (value: I) => void;
3194
+ model?: string; // Model name for internationalization
3195
+ field?: string; // Field name for internationalization
3196
+ validate?: (value: I) => boolean | string;
3197
+ btnClassName?: string;
3198
+ }
3199
+ \`\`\`
3200
+ Key features:
3201
+ - Button-style single selection
3202
+ - Direct Enum type support
3203
+ - Automatic internationalization (l.enum(model, field, item))
3204
+ - Custom button styling
3205
+
3206
+ ### 11. Field.MultiToggleSelect
3207
+
3208
+ \`\`\`typescript
3209
+ interface MultiToggleSelectProps<I> {
3210
+ items: Enum<I> | { label: string; value: I }[] | readonly I[] | I[];
3211
+ value: I[];
3212
+ onChange: (value: I[]) => void;
3213
+ minlength?: number;
3214
+ maxlength?: number;
3215
+ validate?: (value: I[]) => boolean | string;
3216
+ }
3217
+ \`\`\`
3218
+ Key features:
3219
+ - Multiple selection button UI
3220
+ - Minimum/maximum selection limit
3221
+ - Array length validation
3222
+
3223
+ ## Relational Data Selection Components
3224
+
3225
+ ### 12. Field.Parent & Field.ParentId
3226
+
3227
+ \`\`\`typescript
3228
+ interface ParentProps<T, State, Input, Full, Light, Sort, QueryArgs> {
3229
+ sliceName: string; // Target slice name
3230
+ value: Light | null; // Parent returns object
3231
+ onChange: (value?: Light | null) => void;
3232
+ initArgs?: any[]; // Initialization arguments
3233
+ sortOption?: (a: Light, b: Light) => number;
3234
+ renderOption: (model: Light) => ReactNode; // Option renderer
3235
+ renderSelected?: (value: Light) => ReactNode; // Selected item renderer
3236
+ onSearch?: (text: string) => void; // Search handler
3237
+ }
3238
+
3239
+ interface ParentIdProps extends ParentProps {
3240
+ value: string | null; // ParentId returns only ID
3241
+ onChange: (id?: string | null, model?: Light | null) => void;
3242
+ }
3243
+ \`\`\`
3244
+ Key features:
3245
+ - Dynamic store slice integration
3246
+ - Automatic naming convention: \${modelName}List, init\${ModelName}, etc.
3247
+ - Searchable dropdown
3248
+ - Lazy loading (data fetched on onOpen)
3249
+ - Parent: returns full object, ParentId: returns only ID
3250
+
3251
+ ### 13. Field.Children & Field.ChildrenId
3252
+
3253
+ \`\`\`typescript
3254
+ interface ChildrenProps extends ParentProps {
3255
+ value: Light[]; // Multiple selection
3256
+ onChange: (value?: Light[] | null) => void;
3257
+ }
3258
+
3259
+ interface ChildrenIdProps extends ChildrenProps {
3260
+ value: string[]; // ID array
3261
+ onChange: (value: string[]) => void;
3262
+ }
3263
+ \`\`\`
3264
+ Key features:
3265
+ - Multiple relationship selection
3266
+ - Same store integration logic as Parent
3267
+ - Automatic initialization (useEffect)
3268
+
3269
+ ## Date/Time Components
3270
+
3271
+ ### 14. Field.Date
3272
+
3273
+ \`\`\`typescript
3274
+ interface DateProps<Nullable extends boolean> {
3275
+ value: Nullable extends true ? Dayjs | null : Dayjs;
3276
+ onChange: (value: Dayjs) => void;
3277
+ min?: Dayjs;
3278
+ max?: Dayjs;
3279
+ showTime?: boolean; // datetime-local vs date
3280
+ nullable?: boolean;
3281
+ }
3282
+ \`\`\`
3283
+ Key features:
3284
+ - Uses Dayjs objects
3285
+ - Conditional nullable type (using TypeScript generics)
3286
+ - Automatic format change based on showTime
3287
+ - Uses HTML5 date/datetime-local inputs
3288
+ - Includes comment about DaisyUI max value bug
3289
+
3290
+ ### 15. Field.DateRange
3291
+
3292
+ \`\`\`typescript
3293
+ interface DateRangeProps<Nullable extends boolean> {
3294
+ from: Nullable extends true ? Dayjs | null : Dayjs;
3295
+ to: Nullable extends true ? Dayjs | null : Dayjs;
3296
+ onChangeFrom: (value: Dayjs) => void;
3297
+ onChangeTo: (value: Dayjs) => void;
3298
+ onChange?: (from: Dayjs, to: Dayjs) => void; // Optional unified handler
3299
+ showTime?: boolean;
3300
+ }
3301
+ \`\`\`
3302
+ Key features:
3303
+ - Individual management of start/end dates
3304
+ - Optional unified change handler
3305
+ - Automatic "From"/"To" label display
3306
+ - Responsive layout (vertical on mobile)
3307
+
3308
+ ## File Upload Components
3309
+
3310
+ ### 16. Field.Img
3311
+
3312
+ \`\`\`typescript
3313
+ interface ImageProps {
3314
+ sliceName: string; // Determines file upload API
3315
+ value: cnst.File | null;
3316
+ onChange: (file: cnst.File | null) => void;
3317
+ styleType?: "circle" | "square"; // Default: "circle"
3318
+ aspectRatio?: number[]; // Aspect ratio restriction
3319
+ render?: (file: cnst.File) => ReactNode;
3320
+ disabled?: boolean;
3321
+ }
3322
+ \`\`\`
3323
+ Key features:
3324
+ - Dynamic upload API: add\${capitalize(sliceName)}Files
3325
+ - Upload status polling (1-second interval)
3326
+ - Aspect ratio restriction support
3327
+ - Circular/square preview
3328
+
3329
+ ### 17. Field.Imgs
3330
+
3331
+ \`\`\`typescript
3332
+ interface ImagesProps {
3333
+ sliceName: string;
3334
+ value: cnst.File[];
3335
+ onChange: (files: cnst.File[]) => void;
3336
+ minlength?: number; // Default: 1
3337
+ maxlength?: number; // Default: 30
3338
+ render?: (file: cnst.File) => ReactNode;
3339
+ }
3340
+ \`\`\`
3341
+ Key features:
3342
+ - Multiple image upload
3343
+ - Batch polling of upload status
3344
+ - Minimum/maximum file count limit
3345
+ - Fixed square style
3346
+
3347
+ ### 18. Field.File & Field.Files
3348
+
3349
+ \`\`\`typescript
3350
+ interface FileProps {
3351
+ sliceName: string;
3352
+ value: cnst.File | null; // File is single
3353
+ onChange: (file: cnst.File | null) => void;
3354
+ render?: (file: cnst.File) => ReactNode;
3355
+ }
3356
+
3357
+ interface FilesProps {
3358
+ sliceName: string;
3359
+ value: cnst.File[]; // Files is multiple
3360
+ onChange: (files: cnst.File[]) => void;
3361
+ minlength?: number; // Default: 1
3362
+ maxlength?: number; // Default: 30
3363
+ }
3364
+ \`\`\`
3365
+ Key features:
3366
+ - Support for all file types (beyond images)
3367
+ - Same upload logic as Img/Imgs
3368
+ - Custom file renderer support
3369
+
3370
+ ## Rich Text Editor
3371
+
3372
+ ### 19. Field.Slate
3373
+
3374
+ \`\`\`typescript
3375
+ interface SlateProps {
3376
+ sliceName: string; // Determines file upload API
3377
+ valuePath: string; // Store path
3378
+ onChange: (value: unknown) => void;
3379
+ addFile: (file: cnst.File | cnst.File[], options?) => void;
3380
+ placeholder?: string;
3381
+ disabled?: boolean;
3382
+ editorHeight?: string;
3383
+ }
3384
+ \`\`\`
3385
+ Key features:
3386
+ - Slate.js-based rich text editor
3387
+ - File drag and drop support
3388
+ - Dynamic store path access
3389
+ - Adjustable height
3390
+
3391
+ ### 20. Field.Yoopta
3392
+
3393
+ \`\`\`typescript
3394
+ interface YooptaProps {
3395
+ value: JSON;
3396
+ onChange: (value: JSON) => void;
3397
+ readonly?: boolean;
3398
+ }
3399
+ \`\`\`
3400
+ Key features:
3401
+ - Yoopta editor integration
3402
+ - JSON data format
3403
+ - Read-only mode support
3404
+
3405
+ ## Authentication and Contact Components
3406
+
3407
+ ### 21. Field.Email
3408
+
3409
+ \`\`\`typescript
3410
+ interface EmailProps {
3411
+ value: string | null;
3412
+ onChange: (value: string) => void;
3413
+ placeholder?: string; // Default: "example@email.com"
3414
+ minlength?: number; // Default: nullable ? 0 : 2
3415
+ maxlength?: number; // Default: 80
3416
+ inputStyleType?: "bordered" | "borderless" | "underline";
3417
+ cache?: boolean;
3418
+ }
3419
+ \`\`\`
3420
+ Key features:
3421
+ - Automatic email format validation
3422
+ - Uses Input.Email component
3423
+ - Cache key: \${label}-\${desc}-email
3424
+
3425
+ ### 22. Field.Phone
3426
+
3427
+ \`\`\`typescript
3428
+ interface PhoneProps {
3429
+ value: string | null;
3430
+ onChange: (value: string) => void;
3431
+ maxlength?: number; // Default: 13
3432
+ transform?: (value: string) => string; // Default: formatPhone
3433
+ cache?: boolean;
3434
+ }
3435
+ \`\`\`
3436
+ Key features:
3437
+ - Automatic phone number formatting (formatPhone)
3438
+ - isPhoneNumber validation
3439
+ - Cache key: \${label}-\${desc}-phone
3440
+
3441
+ ### 23. Field.Password
3442
+
3443
+ \`\`\`typescript
3444
+ interface PasswordProps {
3445
+ value: string | null;
3446
+ onChange: (value: string) => void;
3447
+ confirmValue?: string | null; // Confirmation input
3448
+ onChangeConfirm?: (value: string) => void;
3449
+ showConfirm?: boolean; // Show confirmation input
3450
+ minlength?: number; // Default: nullable ? 0 : 8
3451
+ maxlength?: number; // Default: 20
3452
+ cache?: boolean;
3453
+ }
3454
+ \`\`\`
3455
+ Key features:
3456
+ - Optional password confirmation input
3457
+ - Automatic match validation
3458
+ - Secure input (masking)
3459
+ - Cache key: \${label}-\${desc}-password
3460
+
3461
+ ## Geographic Location Components
3462
+
3463
+ ### 24. Field.Coordinate
3464
+
3465
+ \`\`\`typescript
3466
+ interface CoordinateProps {
3467
+ coordinate: cnst.util.Coordinate | null;
3468
+ onChange: (coordinate: cnst.util.Coordinate) => void;
3469
+ mapKey: string; // Google Maps API key
3470
+ mapClassName?: string;
3471
+ disabled?: boolean;
3472
+ }
3473
+ \`\`\`
3474
+ Key features:
3475
+ - Google Maps integration
3476
+ - Coordinate selection by clicking
3477
+ - Automatic marker display (AiTwotoneEnvironment icon)
3478
+ - Default zoom level 3
3479
+
3480
+ ### 25. Field.Postcode
3481
+
3482
+ \`\`\`typescript
3483
+ interface PostcodeProps {
3484
+ kakaoKey: string; // Kakao API key
3485
+ address: string | null;
3486
+ onChange: ({
3487
+ address: string;
3488
+ addressEn: string;
3489
+ zipcode: string;
3490
+ coordinate: cnst.util.Coordinate;
3491
+ }) => void;
3492
+ }
3493
+ \`\`\`
3494
+ Key features:
3495
+ - Daum postcode service (react-daum-postcode)
3496
+ - Coordinate conversion using Kakao Maps API
3497
+ - Simultaneous Korean/English address provision
3498
+ - Modal address search
3499
+
3500
+ ### 26. Field.KoreanCityDistrict
3501
+
3502
+ \`\`\`typescript
3503
+ interface KoreanCityDistrictProps {
3504
+ city: string | null;
3505
+ onChangeCity: (city: string | null) => void;
3506
+ district: string | null;
3507
+ onChangeDistrict: (district: string | null) => void;
3508
+ disabled?: boolean;
3509
+ }
3510
+ \`\`\`
3511
+ Key features:
3512
+ - Hardcoded Korean region data
3513
+ - Two-level selection (city/province \u2192 district/county)
3514
+ - Includes detailed regions: 25 districts in Seoul, 16 in Busan, etc.
3515
+ - Linked selection (district activates after city selection)
3516
+
3517
+ ## Common Patterns and Features
3518
+
3519
+ ### 1. Caching System
3520
+
3521
+ Most input components support form data persistence with the cache prop:
3522
+ cacheKey={cache ? \`\${label}-\${desc}-\${componentType}\` : undefined}
3523
+
3524
+ ### 2. Validation System
3525
+
3526
+ All components support multi-layer validation:
3527
+ - Basic validation (length, type, format)
3528
+ - Custom validation functions
3529
+ - Internationalized error messages
3530
+
3531
+ ### 3. Internationalization
3532
+
3533
+ - Labels, placeholders, error messages all use l() function
3534
+ - Automatic translation for Enum types
3535
+ - Multi-language error message templates
3536
+
3537
+ ### 4. Store Integration
3538
+
3539
+ Relational components automatically connect to the store through naming conventions:
3540
+ \`\`\`javascript
3541
+ const names = {
3542
+ model: modelName,
3543
+ modelList: \`\${modelName}List\`,
3544
+ initModel: \`init\${ModelName}\`,
3545
+ };
3546
+ \`\`\`
3547
+
3548
+ ### 5. Type Safety
3549
+
3550
+ - Type safety ensured with TypeScript generics
3551
+ - Conditional types for handling nullable properties
3552
+ - Strict props interface definitions
3553
+
3554
+ This comprehensive field library provides a consistent user experience and developer convenience, designed to make complex form compositions simple and safe.
3555
+ 2. Data Components (/Data/)
3556
+
3557
+ Purpose: Complete data visualization and management interfaces
3558
+
3559
+ ListContainer
3560
+
3561
+ - type?: "card" | "list" - Display mode selection
3562
+ - columns?: DataColumn<any>[] - Column configuration
3563
+ - tools?: DataTool[] - Toolbar actions
3564
+ - renderDashboard?: (props) => ReactNode - Dashboard customization
3565
+ - renderItem?: (props) => ReactNode - Item renderer
3566
+
3567
+ CardList
3568
+
3569
+ - sliceName: string - Store slice identifier
3570
+ - columns: DataColumn<any>[] - Data column definitions
3571
+ - actions?: DataAction<Light>[] - Item actions
3572
+ - renderItem: (args) => ReactNode - Card renderer
3573
+ - renderLoading?: () => ReactNode - Loading state
3574
+
3575
+ Dashboard
3576
+
3577
+ - summary: { [key: string]: any } - Statistics data
3578
+ - queryMap: { [key: string]: any } - Filter mappings
3579
+ - columns?: string[] - Clickable statistics
3580
+ - hidePresents?: boolean - Display control
3581
+
3582
+ 3. Load Components (/Load/)
3583
+
3584
+ Purpose: Data loading and state management with SSR/CSR support
3585
+
3586
+ Page
3587
+
3588
+ - loader: () => Promise<Return> - Data fetching function
3589
+ - render: (data: Return) => ReactNode - Content renderer
3590
+ - loading?: () => ReactNode - Loading state
3591
+
3592
+ Edit
3593
+
3594
+ - edit: ClientEdit<T, Full> | Partial<Full> - Edit data
3595
+ - type?: "modal" | "form" | "empty" - Display mode
3596
+ - sliceName: string - Store slice
3597
+ - onSubmit?: string | ((model: Full) => void) - Submit handler
3598
+
3599
+ Units
3600
+
3601
+ - init: ClientInit<T, L> - Initial data
3602
+ - filter?: (item: L, idx: number) => boolean - Item filtering
3603
+ - sort?: (a: L, b: L) => number - Sorting function
3604
+ - renderItem?: (item: L, idx: number) => ReactNode - Item renderer
3605
+ - pagination?: boolean - Pagination control
3606
+
3607
+ 4. Model Components (/Model/)
3608
+
3609
+ Purpose: CRUD operations with modal and inline editing
3610
+
3611
+ EditModal
3612
+
3613
+ - sliceName: string - Store slice identifier
3614
+ - id?: string - Model ID for editing
3615
+ - renderTitle?: ((model: Full) => ReactNode) | string - Title customization
3616
+ - onSubmit?: string | ((model: Full) => void) - Submit handler
3617
+ - onCancel?: string | ((form?: any) => any) - Cancel handler
3618
+
3619
+ ViewModal
3620
+
3621
+ - id: string - Model identifier
3622
+ - renderView: (model: any) => ReactNode - View content renderer
3623
+ - renderAction?: (model: any) => ReactNode - Action buttons
3624
+
3625
+ NewWrapper
3626
+
3627
+ - sliceName: string - Store slice
3628
+ - partial?: Partial<Full> - Default values
3629
+ - modal?: string | null - Modal type
3630
+
3631
+ 5. System Components (/System/)
3632
+
3633
+ Purpose: Application-level providers and system utilities
3634
+
3635
+ Provider (CSR)
3636
+
3637
+ - fonts: ReactFont[] - Font configurations
3638
+ - gaTrackingId?: string - Analytics tracking
3639
+ - layoutStyle?: "mobile" | "web" - Layout mode
3640
+
3641
+ Provider (SSR)
3642
+
3643
+ - fonts?: NextFont[] - Next.js font configurations
3644
+
3645
+ SelectLanguage
3646
+
3647
+ - languages?: string[] - Available languages
3648
+
3649
+ 6. Only Components (/Only/)
3650
+
3651
+ Purpose: Conditional rendering based on user state and device
3652
+
3653
+ Admin
3654
+
3655
+ - roles?: cnst.AdminRole[] - Required admin roles
3656
+
3657
+ User
3658
+
3659
+ - roles?: cnst.UserRole[] - Required user roles
3660
+
3661
+ Show
3662
+
3663
+ - show?: boolean | cnst.util.Responsive[] - Show conditions
3664
+
3665
+ Mobile/Web
3666
+
3667
+ - No props - Device-based rendering
3668
+
3669
+ 7. Editor Components (/Editor/)
3670
+
3671
+ Purpose: Rich text editing capabilities
3672
+
3673
+ Slate
3674
+
3675
+ - addFilesGql: (fileList: FileList, id?: string) => Promise<File[]> - File upload
3676
+ - addFile: (file: cnst.File | cnst.File[], options?) => void - File management
3677
+ - onChange: (value: unknown) => void - Content change handler
3678
+ - defaultValue?: unknown - Initial content
3679
+ - placeholder?: string - Placeholder text
3680
+ - disabled?: boolean - Read-only mode
3681
+
3682
+ SlateContent
3683
+
3684
+ - content: unknown - Slate content to display
3685
+
3686
+ 8. Property Component (Property.tsx)
3687
+
3688
+ Purpose: Metadata-driven property editing
3689
+
3690
+ - prop: string - Property name
3691
+ - slice: StoreOf<any, any> - Store slice
3692
+ - renderTemplate?: (form: any) => ReactNode - Custom edit renderer
3693
+ - renderView?: (model: any) => ReactNode - Custom view renderer
3694
+ - modelPath?: string - Model path in store
3695
+
3696
+ Integration Patterns
3697
+
3698
+ Store Integration
3699
+
3700
+ - Components use standardized naming conventions for store slices
3701
+ - Automatic state management through slice integration
3702
+ - Consistent error handling and loading states
3703
+
3704
+ Validation System
3705
+
3706
+ - Built-in validation for all field types
3707
+ - Custom validation function support
3708
+ - Internationalized error messages
3709
+
3710
+ File Management
3711
+
3712
+ - Integrated file upload system
3713
+ - Progress tracking and status management
3714
+ - Multiple file type support with preview
3715
+
3716
+ Responsive Behavior
3717
+
3718
+ - Mobile-first design approach
3719
+ - Adaptive layouts based on screen size
3720
+ - Touch-friendly interactions
3721
+
3722
+ This comprehensive UI library provides everything needed to build sophisticated data management interfaces with consistent user
3723
+ experience and maintainable code architecture.
3724
+ `;
3981
3725
  var frameworkAbstract = `
3982
3726
  Intro
3983
3727
  - Build an all-stack application at once.
@@ -4057,6 +3801,87 @@ Core ESLint Extensions
4057
3801
  This configuration creates a robust linting setup that enforces Next.js App Router best practices, maintains clean code
4058
3802
  organization, and ensures proper server/client code separation.
4059
3803
  `;
3804
+ var componentDefaultDescription = ({
3805
+ modelName,
3806
+ ModelName,
3807
+ exampleFiles,
3808
+ constant,
3809
+ properties
3810
+ }) => `
3811
+
3812
+
3813
+ 1. Akan.js \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uAC1C\uC694
3814
+ ${frameworkAbstract}
3815
+
3816
+ 2. Akan.js\uD504\uB808\uC784\uC6CC\uD06C \uB370\uC774\uD130 \uAE30\uBC18 \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uC124\uBA85
3817
+ ${moduleDesription}
3818
+
3819
+ 3. Akan.js eslint \uC124\uC815\uC5D0 \uB300\uD55C \uC124\uBA85
3820
+ ${eslintDescription}
3821
+
3822
+ 4. util/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
3823
+ ${utilUiDescription}
3824
+
3825
+ 5. shared/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
3826
+ ${shardUiDescription}
3827
+
3828
+ 6. ${ModelName}.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uC815\uBCF4
3829
+ ${constant}
3830
+
3831
+ 7. ${modelName}\uC5D0\uC11C \uC885\uC18D\uB418\uB294 \uB2E4\uB978 \uBAA8\uB378\uB4E4\uC758 \uD0C0\uC785 \uC815\uC758
3832
+ ${properties.map(
3833
+ (property) => `
3834
+ \`\`\`
3835
+ ${property.key}.constant.ts
3836
+
3837
+
3838
+ ${property.source}
3839
+ \`\`\`
3840
+ `
3841
+ ).join("\n\n")}
3842
+
3843
+
3844
+ 8. \uC608\uC2DC \uD30C\uC77C\uB4E4
3845
+ ${exampleFiles.map(
3846
+ (example) => `
3847
+ Example filename: ${example.filepath}
3848
+ \`\`\`
3849
+ ${example.content}
3850
+ \`\`\`
3851
+ `
3852
+ ).join("\n\n")}
3853
+
3854
+
3855
+
3856
+
3857
+
3858
+
3859
+ \uC5ED\uD560\uBD80\uC5EC
3860
+ - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
3861
+
3862
+ \uCF54\uB529 \uADDC\uCE59
3863
+ - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3864
+ - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3865
+ - CSS: tailwind, DaisyUI(btn, input, badge \uAC19\uC740 \uAE30\uBCF8 \uC694\uC18C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5, card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
3866
+ - Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3867
+ - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3868
+ \uCF54\uB4DC \uC2A4\uD0C0\uC77C
3869
+ - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
3870
+ - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
3871
+ - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
3872
+ - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
3873
+ \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
3874
+
3875
+ \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
3876
+ - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
3877
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
3878
+ - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
3879
+ - dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 @akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
3880
+
3881
+
3882
+
3883
+
3884
+ `;
4060
3885
  var scalarConstantDescription = `
4061
3886
  Purpose and Structure
4062
3887
 
@@ -4440,7 +4265,7 @@ Target filename: ${modelName}.constant.ts
4440
4265
  ${boilerplate}
4441
4266
  \`\`\`
4442
4267
  `;
4443
- var requestView = ({
4268
+ var requestTemplate = ({
4444
4269
  sysName,
4445
4270
  modelName,
4446
4271
  ModelName,
@@ -4449,78 +4274,56 @@ var requestView = ({
4449
4274
  properties,
4450
4275
  exampleFiles
4451
4276
  }) => `
4452
-
4453
-
4454
- 1. Akan.js \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uAC1C\uC694
4455
- ${frameworkAbstract}
4456
-
4457
- 2. Akan.js\uD504\uB808\uC784\uC6CC\uD06C \uB370\uC774\uD130 \uAE30\uBC18 \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uC124\uBA85
4458
- ${moduleDesription}
4459
-
4460
- 3. Akan.js eslint \uC124\uC815\uC5D0 \uB300\uD55C \uC124\uBA85
4461
- ${eslintDescription}
4462
-
4463
- 4. util/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
4464
- ${utilUiDescription}
4465
-
4466
-
4467
-
4468
- 5. ${ModelName}.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uC815\uBCF4
4469
- ${constant}
4277
+ ${componentDefaultDescription({
4278
+ sysName,
4279
+ modelName,
4280
+ ModelName,
4281
+ exampleFiles,
4282
+ constant,
4283
+ properties
4284
+ })}
4285
+ \uC694\uCCAD\uC0AC\uD56D
4286
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
4287
+ - ${ModelName}.Template.tsx \uCF54\uB4DC \uC791\uC131
4288
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC740 \uBAA8\uB378 \uC774\uB984\uC740 \uC2A4\uD0A4\uB9C8\uC5D0 \uAE30\uBC18\uD55C \uAE30\uB2A5\uC5D0 \uCD08\uC810\uC744 \uB450\uACE0 \uC791\uC131
4289
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
4290
+ - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4291
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
4292
+ -
4470
4293
 
4471
- 6. ${modelName}\uC5D0\uC11C \uC885\uC18D\uB418\uB294 \uB2E4\uB978 \uBAA8\uB378\uB4E4\uC758 \uD0C0\uC785 \uC815\uC758
4472
- ${properties.map(
4473
- (property) => `
4294
+ Application name: ${sysName}
4295
+ Model name: ${modelName}
4296
+ Target filename: ${ModelName}.Template.tsx
4474
4297
  \`\`\`
4475
- ${property.key}.constant.ts
4476
4298
 
4477
-
4478
- ${property.source}
4479
4299
  \`\`\`
4480
- `
4481
- ).join("\n\n")}
4482
4300
 
4483
-
4484
- 7. \uC608\uC2DC \uD30C\uC77C\uB4E4
4485
- ${exampleFiles.map(
4486
- (example) => `
4487
- Example filename: ${example.filepath}
4488
- \`\`\`
4489
- ${example.content}
4490
- \`\`\`
4491
- `
4492
- ).join("\n\n")}
4493
-
4494
-
4495
-
4496
-
4497
-
4498
-
4499
- \uC5ED\uD560\uBD80\uC5EC
4500
- - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C
4501
-
4502
- \uCF54\uB529 \uADDC\uCE59
4503
- - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4504
- - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4505
- - CSS: DaisyUI (btn, input, badge \uAC19\uC740 \uAE30\uBCF8 \uC694\uC18C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5, card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uBD88\uAC00)
4506
- - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4507
- \uCF54\uB4DC \uC2A4\uD0C0\uC77C
4508
- - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
4509
- - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
4510
- - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
4511
- - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
4512
- \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
4513
-
4514
- \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
4515
- - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
4516
- - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
4517
- - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
4518
-
4301
+
4302
+ `;
4303
+ var requestView = ({
4304
+ sysName,
4305
+ modelName,
4306
+ ModelName,
4307
+ boilerplate,
4308
+ constant,
4309
+ properties,
4310
+ exampleFiles
4311
+ }) => `
4312
+ ${componentDefaultDescription({
4313
+ sysName,
4314
+ modelName,
4315
+ ModelName,
4316
+ exampleFiles,
4317
+ constant,
4318
+ properties
4319
+ })}
4519
4320
  \uC694\uCCAD\uC0AC\uD56D
4520
- ${ModelName}.View.tsx \uCF54\uB4DC \uC791\uC131
4521
- \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC740 \uBAA8\uB378 \uC774\uB984\uC740 \uC81C\uC678\uD55C \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uB514\uC790\uC778\uC5D0 \uCD08\uC810\uC744 \uB450\uACE0 \uC791\uC131
4522
- \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
4523
- \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4321
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
4322
+ - ${ModelName}.View.tsx \uCF54\uB4DC \uC791\uC131
4323
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
4324
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
4325
+ - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4326
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
4524
4327
 
4525
4328
 
4526
4329
  Application name: ${sysName}
@@ -4528,7 +4331,7 @@ Model name: ${modelName}
4528
4331
 
4529
4332
  Target filename: ${ModelName}.View.tsx
4530
4333
  \`\`\`
4531
-
4334
+ ${boilerplate}
4532
4335
  \`\`\`
4533
4336
 
4534
4337
 
@@ -4539,78 +4342,25 @@ var requestUnit = ({
4539
4342
  ModelName,
4540
4343
  constant,
4541
4344
  properties,
4345
+ boilerplate,
4542
4346
  exampleFiles
4543
4347
  }) => `
4544
-
4545
-
4546
- 1. Akan.js \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uAC1C\uC694
4547
- ${frameworkAbstract}
4548
-
4549
- 2. Akan.js\uD504\uB808\uC784\uC6CC\uD06C \uB370\uC774\uD130 \uAE30\uBC18 \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uC124\uBA85
4550
- ${moduleDesription}
4551
-
4552
- 3. Akan.js eslint \uC124\uC815\uC5D0 \uB300\uD55C \uC124\uBA85
4553
- ${eslintDescription}
4554
-
4555
- 4. util/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
4556
- ${utilUiDescription}
4557
-
4558
- 5. ${ModelName}.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uC815\uBCF4
4559
- ${constant}
4560
-
4561
- 6. ${modelName}\uC5D0\uC11C \uC885\uC18D\uB418\uB294 \uB2E4\uB978 \uBAA8\uB378\uB4E4\uC758 \uD0C0\uC785 \uC815\uC758
4562
- ${properties.map(
4563
- (property) => `
4564
- \`\`\`
4565
- ${property.key}.constant.ts
4566
-
4567
-
4568
- ${property.source}
4569
- \`\`\`
4570
- `
4571
- ).join("\n\n")}
4572
-
4573
-
4574
- 7. \uC608\uC2DC \uD30C\uC77C\uB4E4
4575
- ${exampleFiles.map(
4576
- (example) => `
4577
- Example filename: ${example.filepath}
4578
- \`\`\`
4579
- ${example.content}
4580
- \`\`\`
4581
- `
4582
- ).join("\n\n")}
4583
-
4584
-
4585
-
4586
-
4587
-
4588
-
4589
- \uC5ED\uD560\uBD80\uC5EC
4590
- - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
4348
+ ${componentDefaultDescription({
4349
+ sysName,
4350
+ modelName,
4351
+ ModelName,
4352
+ exampleFiles,
4353
+ constant,
4354
+ properties
4355
+ })}
4591
4356
 
4592
- \uCF54\uB529 \uADDC\uCE59
4593
- - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4594
- - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4595
- - CSS: DaisyUI (btn, input, badge \uAC19\uC740 \uAE30\uBCF8 \uC694\uC18C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5, card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uBD88\uAC00)
4596
- - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4597
- \uCF54\uB4DC \uC2A4\uD0C0\uC77C
4598
- - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
4599
- - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
4600
- - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
4601
- - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
4602
- \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
4603
-
4604
- \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
4605
- - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
4606
- - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
4607
- - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
4608
-
4609
4357
  \uC694\uCCAD\uC0AC\uD56D
4358
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
4610
4359
  - ${ModelName}.Unit.tsx \uCF54\uB4DC \uC791\uC131
4611
- - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ModelName\uC740 \uC0DD\uB7B5\uD558\uBA70, \uC608\uC2DC\uD30C\uC77C\uB4E4\uC758 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCC38\uACE0\uD558\uC5EC \uC791\uBA85
4360
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
4612
4361
  - \uC608\uC2DC\uD30C\uC77C \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAE30\uBC18\uD558\uC5EC \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uC640 \uB514\uC790\uC778\uC801 \uC694\uC18C\uAC00 \uD3EC\uD568\uB41C \uCEF4\uD3EC\uB10C\uD2B8 3\uAC1C \uAC1C\uBC1C
4613
4362
  - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4363
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
4614
4364
 
4615
4365
 
4616
4366
  Application name: ${sysName}
@@ -4618,7 +4368,7 @@ Model name: ${modelName}
4618
4368
 
4619
4369
  Target filename: ${ModelName}.Unit.tsx
4620
4370
  \`\`\`
4621
-
4371
+ ${boilerplate}
4622
4372
  \`\`\`
4623
4373
 
4624
4374
 
@@ -5869,6 +5619,31 @@ var ModuleRunner = class {
5869
5619
  }
5870
5620
  };
5871
5621
  }
5622
+ async createComponentTemplate(sys2, name, type) {
5623
+ const akanConfig = await sys2.getConfig();
5624
+ const scanResult = await sys2.scan({ akanConfig });
5625
+ await sys2.applyTemplate({
5626
+ basePath: `./lib/${name}__`,
5627
+ template: `module/__Model__.${capitalize(type)}.ts`,
5628
+ scanResult,
5629
+ dict: { model: name, Model: capitalize(name), appName: sys2.name }
5630
+ });
5631
+ await sys2.scan({ akanConfig });
5632
+ return {
5633
+ component: {
5634
+ filename: `${name}.${capitalize(type)}.tsx`,
5635
+ content: sys2.readFile(`lib/${name}__/${capitalize(name)}.${capitalize(type)}.tsx`)
5636
+ }
5637
+ // constant: {
5638
+ // filename: `${name}.constant.ts`,
5639
+ // content: sys.readFile(`lib/__scalar/${name}/${name}.constant.ts`),
5640
+ // },
5641
+ // dictionary: {
5642
+ // filename: `${name}.dictionary.ts`,
5643
+ // content: sys.readFile(`lib/__scalar/${name}/${name}.dictionary.ts`),
5644
+ // },
5645
+ };
5646
+ }
5872
5647
  async createModuleTemplate(sys2, name) {
5873
5648
  const akanConfig = await sys2.getConfig();
5874
5649
  const scanResult = await sys2.scan({ akanConfig });
@@ -5923,64 +5698,6 @@ var ModuleRunner = class {
5923
5698
  }
5924
5699
  };
5925
5700
  }
5926
- // async createUnit(sys: App | Lib, modelName: string) {
5927
- // const modelFileData = getModelFileData(sys.cwdPath, modelName);
5928
- // const { paths } = getRelatedCnsts(modelFileData.constantFilePath);
5929
- // const names = { model: modelName, Model: capitalize(modelName), LightModel: `Light${capitalize(modelName)}` };
5930
- // const prompt = `
5931
- // 너는 React, Typescript, TailwindCSS를 기반으로 코딩하는 프론트엔드 개발자야.
5932
- // ${names.Model}이라는 도메인에 대해서 ${names.LightModel} 스키마를 대상으로 react component를 디자인하려고 해. 아래는 현재 보일러플레이트 형태의 컴포넌트야.
5933
- // \`\`\`
5934
- // ${modelFileData.unitFileStr}
5935
- // \`\`\`
5936
- // ${names.LightModel}은 다음과 같이 ${names.Model} 스키마에서 일부 필드를 추출해서 lightweight fetch된 형태로 설계되어있어.
5937
- // \`\`\`
5938
- // ${modelFileData.constantFileStr}
5939
- // \`\`\`
5940
- // 아래는 현재 스키마와 연관된 스키마 파일들이야. 스키마 파일들의 코드들을 보고 서로 어떻게 연계 되어있는지 이해하도록 해.
5941
- // ${
5942
- // paths
5943
- // ? new Array(paths.size).fill(0).map((_, index) => {
5944
- // //순서대로 paths key 추출
5945
- // const key = Array.from(paths.keys())[index];
5946
- // const filePath = paths.get(key)?.filePath;
5947
- // if (!filePath) throw new Error("filePath is undefined");
5948
- // return `${key}\n\`\`\`${fs.readFileSync(filePath, "utf8")}\`\`\`\`\n\n`;
5949
- // })
5950
- // : ""
5951
- // }
5952
- // 추가로, 만약에 아이콘 사용이 필요하면 react-icons/bi 라이브러리에서 사용해줘.
5953
- // 또, 색상을 사용하려고 하면 하드코딩된 색상(bg-red)이 아닌 테마 색상(bg-primary)을 사용해서 작성해줘.
5954
- // 그리고 optional한 필드는 field && <div>... 가 아닌, field ? <div>... : null 형태로 작성해줘.
5955
- // css라이브러리는 DaisyUI를 기반으로 작성해주는데, btn, input, badge와 같은 단순한 기본 css는 사용해도 괜찮아. 그런데 card, hero같이 복잡한 컴포넌트는 사용하면 안돼.
5956
- // 조건부 className이 필요한 경우에는 clsx 라이브러리를 사용해서 작성해야해.
5957
- // 모델에 대해서 object destructuring은 하지말고 ${modelName}.field 형태로 접근하게 코드를 작성해야해.
5958
- // ${names.Model}.Unit.Card의 리액트 컴포넌트를 디자인해서 작성해줘.
5959
- // `;
5960
- // try {
5961
- // fs.writeFileSync("./local/prompt.txt", prompt);
5962
- // const { content } = await streamAi(prompt, (chunk) => {
5963
- // process.stdout.write(chunk);
5964
- // });
5965
- // fs.writeFileSync("./local/result.txt", content);
5966
- // } catch (error) {
5967
- // // console.error("Application error:", error);
5968
- // }
5969
- // }
5970
- // async createView(sys: App | Lib, modelName: string) {
5971
- // const modelFileData = getModelFileData(sys.cwdPath, modelName);
5972
- // // const modelFileData = getModelFileData(sys.cwdPath, modelName);
5973
- // // const { paths } = getRelatedCnsts(modelFileData.constantFilePath);
5974
- // // const names = { model: modelName, Model: capitalize(modelName), LightModel: `Light${capitalize(modelName)}` };
5975
- // // const prompt = prompt.requestView({
5976
- // // sysName: sys.name,
5977
- // // modelName,
5978
- // // modelDesc: modelFileData.modelDesc,
5979
- // // modelSchemaDesign: modelFileData.modelSchemaDesign,
5980
- // // boilerplate: modelFileData.viewFileStr,
5981
- // // paths,
5982
- // // });
5983
- // }
5984
5701
  };
5985
5702
 
5986
5703
  // pkgs/@akanjs/cli/src/module/module.script.ts
@@ -6010,6 +5727,7 @@ var ModuleScript = class {
6010
5727
  async removeModule(workspace, name) {
6011
5728
  }
6012
5729
  async createScalar(sys2, name, description, schemaDescription) {
5730
+ await AiSession.init();
6013
5731
  await AiSession.init();
6014
5732
  const scalarConstantExampleFiles = await sys2.workspace.getScalarConstantFiles();
6015
5733
  const { constant, dictionary } = await this.#runner.createScalarTemplate(sys2, name);
@@ -6030,7 +5748,45 @@ var ModuleScript = class {
6030
5748
  }
6031
5749
  async createTest(workspace, name) {
6032
5750
  }
5751
+ async createTemplate(sys2) {
5752
+ await AiSession.init();
5753
+ const libs = await sys2.getModules();
5754
+ const lib = await (0, import_prompts8.select)({
5755
+ message: "Select the lib",
5756
+ choices: libs
5757
+ }).catch((e) => {
5758
+ Logger.error("canceled");
5759
+ return null;
5760
+ });
5761
+ if (!lib)
5762
+ return;
5763
+ const name = lib.split("/").pop();
5764
+ if (!name)
5765
+ return;
5766
+ const { component: template } = await this.#runner.createComponentTemplate(sys2, name, "template");
5767
+ const templateExampleFiles = (await sys2.getTemplatesSourceCode()).filter(
5768
+ (f) => !f.filepath.includes(`${name}.Template.tsx`)
5769
+ );
5770
+ const Name = capitalize(name);
5771
+ const relatedCnsts = getRelatedCnsts(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`);
5772
+ const constant = import_fs10.default.readFileSync(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`, "utf-8");
5773
+ const session = new AiSession();
5774
+ const promptRst = requestTemplate({
5775
+ sysName: sys2.name,
5776
+ modelName: name,
5777
+ ModelName: Name,
5778
+ constant,
5779
+ boilerplate: template.content,
5780
+ properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
5781
+ exampleFiles: randomPicks(templateExampleFiles, Math.min(20, templateExampleFiles.length))
5782
+ });
5783
+ const content = await session.editTypescript(promptRst);
5784
+ import_fs10.default.writeFileSync(`${sys2.cwdPath}/promptTemplate.txt`, promptRst);
5785
+ import_fs10.default.writeFileSync(`${sys2.cwdPath}/resultTemplate.txt`, content);
5786
+ sys2.writeFile(`lib/${name}__/${Name}.Template.tsx`, content);
5787
+ }
6033
5788
  async createUnit(sys2) {
5789
+ await AiSession.init();
6034
5790
  const libs = await sys2.getModules();
6035
5791
  const lib = await (0, import_prompts8.select)({
6036
5792
  message: "Select the lib",
@@ -6044,6 +5800,7 @@ var ModuleScript = class {
6044
5800
  const name = lib.split("/").pop();
6045
5801
  if (!name)
6046
5802
  return;
5803
+ const { component: unit } = await this.#runner.createComponentTemplate(sys2, name, "unit");
6047
5804
  const Name = capitalize(name);
6048
5805
  const unitExampleFiles = (await sys2.getUnitsSourceCode()).filter((f) => !f.filepath.includes(`${name}.Unit.tsx`));
6049
5806
  const relatedCnsts = getRelatedCnsts(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`);
@@ -6055,13 +5812,16 @@ var ModuleScript = class {
6055
5812
  ModelName: Name,
6056
5813
  constant,
6057
5814
  properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
6058
- exampleFiles: randomPicks(unitExampleFiles, Math.min(10, unitExampleFiles.length))
5815
+ exampleFiles: randomPicks(unitExampleFiles, Math.min(10, unitExampleFiles.length)),
5816
+ boilerplate: unit.content
6059
5817
  });
6060
5818
  const content = await session.editTypescript(promptRst);
6061
5819
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/promptUnit.txt`, promptRst);
6062
5820
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/resultUnit.txt`, content);
5821
+ sys2.writeFile(`lib/${name}__/${Name}.Unit.tsx`, content);
6063
5822
  }
6064
5823
  async createView(sys2) {
5824
+ await AiSession.init();
6065
5825
  const libs = await sys2.getModules();
6066
5826
  const lib = await (0, import_prompts8.select)({
6067
5827
  message: "Select the lib",
@@ -6075,6 +5835,7 @@ var ModuleScript = class {
6075
5835
  const name = lib.split("/").pop();
6076
5836
  if (!name)
6077
5837
  return;
5838
+ const { component: view } = await this.#runner.createComponentTemplate(sys2, name, "view");
6078
5839
  const viewExampleFiles = (await sys2.getViewsSourceCode()).filter((f) => !f.filepath.includes(`${name}.View.tsx`));
6079
5840
  const Name = capitalize(name);
6080
5841
  const relatedCnsts = getRelatedCnsts(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`);
@@ -6085,12 +5846,14 @@ var ModuleScript = class {
6085
5846
  modelName: name,
6086
5847
  ModelName: Name,
6087
5848
  constant,
5849
+ boilerplate: view.content,
6088
5850
  properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
6089
5851
  exampleFiles: randomPicks(viewExampleFiles, Math.min(20, viewExampleFiles.length))
6090
5852
  });
6091
5853
  const content = await session.editTypescript(promptRst);
6092
5854
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/prompt.txt`, promptRst);
6093
5855
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/result.txt`, content);
5856
+ sys2.writeFile(`lib/${name}__/${Name}.View.tsx`, content);
6094
5857
  }
6095
5858
  };
6096
5859
 
@@ -6098,14 +5861,14 @@ var ModuleScript = class {
6098
5861
  var ModuleCommand = class {
6099
5862
  moduleScript = new ModuleScript();
6100
5863
  async createModule(sys2, name, workspace) {
6101
- await this.moduleScript.createModuleTemplate(sys2, name);
5864
+ await this.moduleScript.createModuleTemplate(sys2, lowerlize(name));
6102
5865
  }
6103
5866
  async removeModule(name, workspace) {
6104
5867
  }
6105
5868
  async scanModule(name, workspace) {
6106
5869
  }
6107
5870
  async createScalar(sys2, name, description, schemaDescription, workspace) {
6108
- await this.moduleScript.createScalar(sys2, name, description, schemaDescription);
5871
+ await this.moduleScript.createScalar(sys2, lowerlize(name), description, schemaDescription);
6109
5872
  }
6110
5873
  async createService(name, workspace) {
6111
5874
  }
@@ -6117,6 +5880,9 @@ var ModuleCommand = class {
6117
5880
  async createUnit(sys2, workspace) {
6118
5881
  await this.moduleScript.createUnit(sys2);
6119
5882
  }
5883
+ async createTemplate(sys2, workspace) {
5884
+ await this.moduleScript.createTemplate(sys2);
5885
+ }
6120
5886
  };
6121
5887
  __decorateClass([
6122
5888
  Target.Public(),
@@ -6162,6 +5928,11 @@ __decorateClass([
6162
5928
  __decorateParam(0, Sys()),
6163
5929
  __decorateParam(1, Workspace())
6164
5930
  ], ModuleCommand.prototype, "createUnit", 1);
5931
+ __decorateClass([
5932
+ Target.Public(),
5933
+ __decorateParam(0, Sys()),
5934
+ __decorateParam(1, Workspace())
5935
+ ], ModuleCommand.prototype, "createTemplate", 1);
6165
5936
  ModuleCommand = __decorateClass([
6166
5937
  Commands()
6167
5938
  ], ModuleCommand);
@@ -6438,5 +6209,5 @@ void runCommands(
6438
6209
  //! 2. csr폴더를 현 위치로 복사 후 압축 후 삭제
6439
6210
  //! execSync를 가져오기 싫으니 일단 2번 방법으로 해보자
6440
6211
  //! add path in tsconfig.json
6441
- //! 파일을 {name}.Unit.tsx에 저장.
6442
6212
  //! 파일을 {name}.View.tsx에 저장.
6213
+ //! 파일을 {name}.Unit.tsx에 저장.