@qlover/create-app 0.7.13 → 0.7.14

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/config/IOCIdentifier.ts +14 -1
  5. package/dist/templates/next-app/config/Identifier/index.ts +1 -0
  6. package/dist/templates/next-app/config/Identifier/page.admin.ts +48 -0
  7. package/dist/templates/next-app/config/i18n/admin18n.ts +33 -0
  8. package/dist/templates/next-app/config/i18n/index.ts +3 -1
  9. package/dist/templates/next-app/migrations/schema/UserSchema.ts +2 -2
  10. package/dist/templates/next-app/next.config.ts +1 -1
  11. package/dist/templates/next-app/package.json +3 -1
  12. package/dist/templates/next-app/public/locales/en.json +8 -1
  13. package/dist/templates/next-app/public/locales/zh.json +8 -1
  14. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +1 -1
  15. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +14 -16
  16. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +10 -3
  17. package/dist/templates/next-app/src/app/[locale]/layout.tsx +1 -1
  18. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
  19. package/dist/templates/next-app/src/app/[locale]/page.tsx +2 -3
  20. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
  21. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +32 -0
  22. package/dist/templates/next-app/src/base/cases/AppConfig.ts +3 -0
  23. package/dist/templates/next-app/src/base/cases/ChatAction.ts +21 -0
  24. package/dist/templates/next-app/src/base/cases/FocusBarAction.ts +36 -0
  25. package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +1 -3
  26. package/dist/templates/next-app/src/base/services/AdminUserService.ts +1 -1
  27. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +1 -1
  28. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +23 -1
  29. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +2 -2
  30. package/dist/templates/next-app/src/base/types/PageProps.ts +1 -1
  31. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +1 -0
  32. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +1 -0
  33. package/dist/templates/next-app/src/core/globals.ts +2 -0
  34. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +4 -1
  35. package/dist/templates/next-app/src/{base/cases → server}/PageParams.ts +1 -1
  36. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +31 -0
  37. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +1 -1
  38. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +6 -4
  39. package/dist/templates/next-app/src/server/services/AIService.ts +43 -0
  40. package/dist/templates/next-app/src/server/services/ApiUserService.ts +1 -1
  41. package/dist/templates/next-app/src/server/{SupabaseBridge.ts → sqlBridges/SupabaseBridge.ts} +16 -11
  42. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +4 -4
  43. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +1 -1
  44. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +32 -25
  45. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +12 -26
  46. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +37 -5
  47. package/dist/templates/next-app/src/uikit/components/ChatRoot.tsx +17 -0
  48. package/dist/templates/next-app/src/uikit/components/ClientSeo.tsx +36 -0
  49. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +5 -6
  50. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +2 -0
  51. package/dist/templates/next-app/src/uikit/components/With.tsx +17 -0
  52. package/dist/templates/next-app/src/uikit/components/chat/ChatActionInterface.ts +30 -0
  53. package/dist/templates/next-app/src/uikit/components/chat/ChatFocusBar.tsx +65 -0
  54. package/dist/templates/next-app/src/uikit/components/chat/ChatMessages.tsx +59 -0
  55. package/dist/templates/next-app/src/uikit/components/chat/ChatWrap.tsx +28 -0
  56. package/dist/templates/next-app/src/uikit/components/chat/FocusBarActionInterface.ts +19 -0
  57. package/package.json +1 -1
  58. package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +0 -21
  59. package/dist/templates/next-app/src/base/port/DBMigrationInterface.ts +0 -92
  60. package/dist/templates/next-app/src/base/port/MigrationApiInterface.ts +0 -3
  61. package/dist/templates/next-app/src/base/port/ServerApiResponseInterface.ts +0 -6
  62. package/dist/templates/next-app/src/base/services/migrations/MigrationsApi.ts +0 -43
  63. package/dist/templates/next-app/config/i18n/{HomeI18n .ts → HomeI18n.ts} +0 -0
  64. package/dist/templates/next-app/{build → make}/generateLocales.ts +2 -2
  65. /package/dist/templates/next-app/src/{base → server}/port/PaginationInterface.ts +0 -0
  66. /package/dist/templates/next-app/src/{base → server}/port/ParamsHandlerInterface.ts +0 -0
@@ -4,17 +4,16 @@ import { TranslationOutlined } from '@ant-design/icons';
4
4
  import { Dropdown } from 'antd';
5
5
  import { useLocale } from 'next-intl';
6
6
  import { useCallback, useMemo } from 'react';
7
- import type {
8
- I18nServiceInterface,
9
- I18nServiceLocale
10
- } from '@/base/port/I18nServiceInterface';
7
+ import type { I18nServiceLocale } from '@/base/port/I18nServiceInterface';
11
8
  import { usePathname, useRouter } from '@/i18n/routing';
12
9
  import { i18nConfig } from '@config/i18n';
13
10
  import type { LocaleType } from '@config/i18n';
11
+ import { I } from '@config/IOCIdentifier';
12
+ import { useIOC } from '../hook/useIOC';
14
13
  import type { ItemType } from 'antd/es/menu/interface';
15
14
 
16
- export function LanguageSwitcher(props: { i18nService: I18nServiceInterface }) {
17
- const { i18nService } = props;
15
+ export function LanguageSwitcher() {
16
+ const i18nService = useIOC(I.I18nServiceInterface);
18
17
  const pathname = usePathname(); // current pathname, aware of i18n
19
18
 
20
19
  const router = useRouter(); // i18n-aware router instance
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import { LogoutOutlined } from '@ant-design/icons';
2
4
  import { Tooltip } from 'antd';
3
5
  import { useCallback } from 'react';
@@ -0,0 +1,17 @@
1
+ export function With<T>(props: {
2
+ fallback?: React.ReactNode;
3
+ it: T;
4
+ children: React.ReactNode | ((it: NonNullable<T>) => React.ReactNode);
5
+ }) {
6
+ const { fallback, it, children } = props;
7
+
8
+ if (it) {
9
+ if (typeof children === 'function') {
10
+ return children(it);
11
+ }
12
+
13
+ return children;
14
+ }
15
+
16
+ return fallback ?? null;
17
+ }
@@ -0,0 +1,30 @@
1
+ import {
2
+ StoreInterface,
3
+ type StoreStateInterface
4
+ } from '@qlover/corekit-bridge';
5
+
6
+ export const MessageType = Object.freeze({
7
+ USER: 'user',
8
+ ASSISTANT: 'assistant'
9
+ });
10
+
11
+ export type MessageTypeValue = (typeof MessageType)[keyof typeof MessageType];
12
+
13
+ export interface MessageInterface {
14
+ id: string;
15
+ content: unknown;
16
+ role: MessageTypeValue;
17
+ createdAt: string;
18
+
19
+ loading?: boolean;
20
+ }
21
+
22
+ export interface ChatStateInterface extends StoreStateInterface {
23
+ messages: MessageInterface[];
24
+ }
25
+
26
+ export abstract class ChatActionInterface<
27
+ S extends ChatStateInterface
28
+ > extends StoreInterface<S> {
29
+ abstract focus(): void;
30
+ }
@@ -0,0 +1,65 @@
1
+ import { SendOutlined } from '@ant-design/icons';
2
+ import { Button, Input } from 'antd';
3
+ import { useCallback, useRef } from 'react';
4
+ import { useStore } from '../../hook/useStore';
5
+ import type {
6
+ FocusBarActionInterface,
7
+ FocusBarStateInterface
8
+ } from './FocusBarActionInterface';
9
+
10
+ export function ChatFocusBar({
11
+ focusBarAction
12
+ }: {
13
+ focusBarAction: FocusBarActionInterface<FocusBarStateInterface>;
14
+ }) {
15
+ const inputRef = useRef<HTMLTextAreaElement>(null);
16
+ const { inputValue } = useStore(focusBarAction);
17
+ const sendState = useStore(focusBarAction, (state) => state.sendState);
18
+
19
+ const handleInputChange = useCallback(
20
+ (e: React.ChangeEvent<HTMLTextAreaElement>) => {
21
+ focusBarAction.setInputValue(e.target.value);
22
+ },
23
+ [focusBarAction]
24
+ );
25
+
26
+ const handleKeyDown = useCallback(
27
+ (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
28
+ if (e.key === 'Enter' && !e.shiftKey) {
29
+ e.preventDefault();
30
+ focusBarAction.sendMessage(inputValue);
31
+ }
32
+ },
33
+ [focusBarAction, inputValue]
34
+ );
35
+
36
+ const sending = sendState.loading;
37
+
38
+ return (
39
+ <div
40
+ data-testid="ChatFocusBarInput"
41
+ className="flex items-end gap-2 p-4 bg-elevated border-t border-c-border"
42
+ >
43
+ <Input.TextArea
44
+ ref={inputRef}
45
+ disabled={sending}
46
+ value={inputValue}
47
+ onChange={handleInputChange}
48
+ onKeyDown={handleKeyDown}
49
+ placeholder="Type your message..."
50
+ rows={1}
51
+ />
52
+ <Button
53
+ data-testid="ChatFocusBarSendButton"
54
+ onClick={() => {
55
+ focusBarAction.sendMessage(inputValue);
56
+ }}
57
+ type="primary"
58
+ className="flex items-center justify-center !h-10 !w-10 !rounded-full !bg-c-brand !text-white hover:!bg-c-brand-hover transition-colors"
59
+ icon={<SendOutlined />}
60
+ loading={sending}
61
+ disabled={sending}
62
+ />
63
+ </div>
64
+ );
65
+ }
@@ -0,0 +1,59 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { MessageType } from './ChatActionInterface';
3
+ import { useStore } from '../../hook/useStore';
4
+ import type {
5
+ ChatActionInterface,
6
+ ChatStateInterface,
7
+ MessageInterface
8
+ } from './ChatActionInterface';
9
+
10
+ function MessageItem({ message }: { message: MessageInterface }) {
11
+ return (
12
+ <div
13
+ data-testid="MessageItem"
14
+ className={`flex ${
15
+ message.role === MessageType.USER ? 'justify-end' : 'justify-start'
16
+ } mb-4`}
17
+ >
18
+ <div
19
+ data-testid="MessageItemContent"
20
+ className={`max-w-[70%] rounded-lg p-3 ${
21
+ message.role === MessageType.USER
22
+ ? 'bg-blue-500 text-white'
23
+ : 'bg-gray-100 dark:bg-gray-700'
24
+ }`}
25
+ >
26
+ <p className="whitespace-pre-wrap break-words">{message.content}</p>
27
+ <div className="mt-1 text-xs opacity-70">
28
+ {new Date(message.createdAt).toLocaleTimeString()}
29
+ </div>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ export function ChatMessages({
36
+ chatAction
37
+ }: {
38
+ chatAction: ChatActionInterface<ChatStateInterface>;
39
+ }) {
40
+ const messagesEndRef = useRef<HTMLDivElement>(null);
41
+ const { messages } = useStore(chatAction);
42
+
43
+ useEffect(() => {
44
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
45
+ }, [messages]);
46
+
47
+ return (
48
+ <div data-testid="ChatMessages" className="flex-1 overflow-y-auto p-4">
49
+ {messages.map((message: MessageInterface) => (
50
+ <MessageItem
51
+ data-testid="MessageItem"
52
+ key={message.id}
53
+ message={message}
54
+ />
55
+ ))}
56
+ <div ref={messagesEndRef} />
57
+ </div>
58
+ );
59
+ }
@@ -0,0 +1,28 @@
1
+ import { ChatFocusBar } from './ChatFocusBar';
2
+ import { ChatMessages } from './ChatMessages';
3
+ import type {
4
+ ChatActionInterface,
5
+ ChatStateInterface
6
+ } from './ChatActionInterface';
7
+ import type {
8
+ FocusBarActionInterface,
9
+ FocusBarStateInterface
10
+ } from './FocusBarActionInterface';
11
+
12
+ export function ChatWrap({
13
+ chatAction,
14
+ focusBarAction
15
+ }: {
16
+ chatAction: ChatActionInterface<ChatStateInterface>;
17
+ focusBarAction: FocusBarActionInterface<FocusBarStateInterface>;
18
+ }) {
19
+ return (
20
+ <div
21
+ data-testid="ChatWrap"
22
+ className="flex h-full flex-col p-2 bg-primary shadow-2xl"
23
+ >
24
+ <ChatMessages chatAction={chatAction} />
25
+ <ChatFocusBar focusBarAction={focusBarAction} />
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1,19 @@
1
+ import {
2
+ StoreInterface,
3
+ type StoreStateInterface
4
+ } from '@qlover/corekit-bridge';
5
+ import type { AsyncStateInterface } from '@/base/port/AsyncStateInterface';
6
+
7
+ export interface FocusBarStateInterface extends StoreStateInterface {
8
+ showHistoryArea: boolean;
9
+ inputValue: string;
10
+ sendState: AsyncStateInterface<unknown>;
11
+ }
12
+
13
+ export abstract class FocusBarActionInterface<
14
+ S extends FocusBarStateInterface
15
+ > extends StoreInterface<S> {
16
+ abstract setInputValue(value: string): void;
17
+ abstract clearInput(): void;
18
+ abstract sendMessage(message: string): void | Promise<void>;
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlover/create-app",
3
- "version": "0.7.13",
3
+ "version": "0.7.14",
4
4
  "description": "Create a new app with a single command",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1,21 +0,0 @@
1
- import type { PostgrestSingleResponse } from '@supabase/supabase-js';
2
-
3
- export type WhereOperation = '=' | '!=' | '>' | '<' | '>=' | '<=';
4
- export type Where = [string, WhereOperation, string | number];
5
-
6
- export interface BridgeEvent {
7
- table: string;
8
- fields?: string | string[];
9
- where?: Where[];
10
- data?: unknown;
11
- page?: number;
12
- pageSize?: number;
13
- }
14
-
15
- export interface DBBridgeInterface {
16
- add(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
17
- update(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
18
- delete(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
19
- get(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
20
- pagination(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
21
- }
@@ -1,92 +0,0 @@
1
- export interface MigrationConfig {
2
- /**
3
- * Migration version number or identifier
4
- */
5
- version: string | number;
6
-
7
- /**
8
- * Description of what this migration does
9
- */
10
- description: string;
11
-
12
- /**
13
- * Timestamp when migration was created
14
- */
15
- timestamp?: number;
16
- }
17
-
18
- export interface MigrationResult {
19
- /**
20
- * Whether the migration was successful
21
- */
22
- success: boolean;
23
-
24
- /**
25
- * Error message if migration failed
26
- */
27
- error?: string;
28
-
29
- /**
30
- * Migration version that was executed
31
- */
32
- version: string | number;
33
-
34
- /**
35
- * Time taken to execute the migration in milliseconds
36
- */
37
- executionTime?: number;
38
- }
39
-
40
- export interface DBMigrationInterface {
41
- /**
42
- * Get current database version
43
- */
44
- getCurrentVersion(): Promise<string | number>;
45
-
46
- /**
47
- * Get list of all applied migrations
48
- */
49
- getAppliedMigrations(): Promise<MigrationConfig[]>;
50
-
51
- /**
52
- * Get list of pending migrations that need to be applied
53
- */
54
- getPendingMigrations(): Promise<MigrationConfig[]>;
55
-
56
- /**
57
- * Apply a specific migration
58
- * @param migration Migration configuration
59
- */
60
- applyMigration(migration: MigrationConfig): Promise<MigrationResult>;
61
-
62
- /**
63
- * Rollback a specific migration
64
- * @param migration Migration configuration
65
- */
66
- rollbackMigration(migration: MigrationConfig): Promise<MigrationResult>;
67
-
68
- /**
69
- * Apply all pending migrations
70
- */
71
- migrateUp(): Promise<MigrationResult[]>;
72
-
73
- /**
74
- * Rollback to a specific version
75
- * @param targetVersion Version to rollback to
76
- */
77
- migrateDown(targetVersion: string | number): Promise<MigrationResult[]>;
78
-
79
- /**
80
- * Create a new migration file/record
81
- * @param name Name/description of the migration
82
- */
83
- createMigration(name: string): Promise<MigrationConfig>;
84
-
85
- /**
86
- * Validate migration history and database state
87
- */
88
- validateMigrations(): Promise<{
89
- valid: boolean;
90
- errors?: string[];
91
- }>;
92
- }
@@ -1,3 +0,0 @@
1
- export interface MigrationApiInterface {
2
- init(): Promise<unknown>;
3
- }
@@ -1,6 +0,0 @@
1
- export interface ServerApiResponseInterface<T> {
2
- success?: boolean;
3
- message?: string;
4
- data?: T;
5
- error?: string;
6
- }
@@ -1,43 +0,0 @@
1
- import { RequestCommonPlugin } from '@qlover/corekit-bridge';
2
- import {
3
- FetchAbortPlugin,
4
- RequestTransaction,
5
- RequestAdapterFetch,
6
- FetchURLPlugin
7
- } from '@qlover/fe-corekit';
8
- import { inject, injectable } from 'inversify';
9
- import type { MigrationApiInterface } from '@/base/port/MigrationApiInterface';
10
- import type { RequestAdapterConfig } from '@qlover/fe-corekit';
11
-
12
- /**
13
- * UserApi
14
- *
15
- * @description
16
- * UserApi is a client for the user API.
17
- *
18
- */
19
- @injectable()
20
- export class MigrationsApi
21
- extends RequestTransaction<RequestAdapterConfig>
22
- implements MigrationApiInterface
23
- {
24
- constructor(
25
- @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
26
- ) {
27
- super(
28
- new RequestAdapterFetch({
29
- baseURL: '/api/admin/migrations',
30
- responseType: 'json'
31
- })
32
- );
33
-
34
- this.usePlugin(new FetchURLPlugin()).usePlugin(new RequestCommonPlugin());
35
- }
36
-
37
- async init(): Promise<unknown> {
38
- return this.request({
39
- url: '/init',
40
- method: 'POST'
41
- });
42
- }
43
- }
@@ -1,7 +1,7 @@
1
+ import { readdirSync } from 'fs';
2
+ import { join } from 'path';
1
3
  import { Ts2Locales } from '@brain-toolkit/ts2locales';
2
4
  import { i18nConfig } from '../config/i18n';
3
- import { join } from 'path';
4
- import { readdirSync } from 'fs';
5
5
 
6
6
  export async function generateLocales() {
7
7
  const locales = i18nConfig.supportedLngs as unknown as string[];