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